Drupal and HTTP requests revisited
UPDATE: Now running Drupal 8. Info is outdated again!
Background: Read this older blogpost.
So I just made a new theme for my blog, and as it turns out, I got one extra HTTP request. I started using Font Awesome with the theme and the file is too big to be embedded as a base64 encoded font. Darnit. So up to 2 internal HTTP requests.
But anyway, I made a new direction in how to cache my css, and the result is way better for mobile.
CSS is looped through (with the core function drupal_load_stylesheet() and then is processed the same way core processes css with aggregation. This way no image paths gets messed up (and the same goes for the path for Font Awesome). Since this is heavy to do on each pageload, I cache the result of this processing, and in the page files I just print out a javascript that you can see if you view the source. The same goes for javascript, a different loop, but the same result. This is for example my CSS function:
<?php$style = '';foreach($styles as $stylesheet) { if (strpos($stylesheet['data'], 'system/system.menus.css') > 0) { // Skip this one, at least for my theme. continue; } // The following are nicked from Drupal core. $contents = drupal_load_stylesheet($stylesheet['data'], TRUE); // Build the base URL of this CSS file: start with the full URL. $css_base_url = file_create_url($stylesheet['data']); // Move to the parent. $css_base_url = substr($css_base_url, 0, strrpos($css_base_url, '/')); // Simplify to a relative URL if the stylesheet URL starts with the // base URL of the website. if (substr($css_base_url, 0, strlen($GLOBALS['base_root'])) == $GLOBALS['base_root']) { $css_base_url = substr($css_base_url, strlen($GLOBALS['base_root'])); } _drupal_build_css_path(NULL, $css_base_url . '/'); // Anchor all paths in the CSS with its base URL, ignoring external and absolute paths. $style .= preg_replace_callback('/url\(\s*[\'"]?(?![a-z]+:|\/+)([^\'")]+)[\'"]?\s*\)/i', '_drupal_build_css_path', $contents);}
And then to the javascript. It is dynamically printed on cache flush, so just view the source to see what it looks like. So what the script does is do a check if you have the css or js for my page cached in localStorage. It is stored inside an object with a key, so I can clear the cache and invalidate your localstorage cache. The key is generated for each flush cache action, with the hook_flush_caches() hook. If you don't have my assets, the CSS is loaded by AJAX, and I do a synchronous GET for the js (don't get me started on why it is synchronous). So how do I avoid showing the page without styling while waiting for the css? Simple. In the head of the page there is a style tag that says #page should be display none. When my css loads, it has rules saying i should show the #page. Done.
So then you think "Man, all this foreach stuff clutters up the template file". Nope. This is the actual lines from html.tpl.php:
print client_cache_styles($css);print client_cache_scripts($scripts);
"And what's with the non-jquery javascript syntax?" you may ask. Naturally this is because the same code also loads my javascripts, and so jquery is not included at that point in the code. And also, writing native javascript is fun.
So this makes for 4 internal HTTP requests on first pageload, and 2 after that.
Expanding this, I also used the same trick on the $page variable, so the entire page was loaded with ajax (or localstorage, if available). So I actually got a full-featured, full styled CMS in a couple of KB and 2 HTTP requests. This has some side effects, obviously, so I skipped that one on the live page.
The module client_cache will probably be made available one of these days, if I get around to it. But it's probably not a good idea for most sites!
Let's finish off with a banana musician!