The Case for Critical Assets

When it comes to first impressions, it can be said a website is made or broken by them. A modern website that relies heavily on scripting is likely self inflicting a performance hit. This hit can be caused by the generic advice given for speeding up a website, ironically. Many web speed tests give an immediate failing grade for not using a content delivery network (CDN) for every single one of your assets. But in the case of critical scripts and styles, this can be the exact wrong advice.

Critical scripts and style are the assets that must load prior to effectively loading content. For instance, a site might use Modernizr to sniff for features to further load content or other scripts. It might use jQuery to create DOM elements and place them on the page. It also might use @media queries to alter, import or change the layout of the page. If any of these situations exist, a new or returning user will have to wait for that file to load on the page prior to being able to view any content. I’m not going to argue if a site needs to be doing those things first, but if it is, then it’s going to cause a ‘hiccup’ for first time rendering. A cached asset may negate this on further loads and that’s why caching and cache control is a good thing to use. But this does nothing to help a first impression.

[caption id=”attachment_571” align=”aligncenter” width=”500”]DNS lookup times are killing time DNS lookups are killing time[/caption]

Take a simple example page. Upon loading there could be a significant Domain Name Server (DNS) lookup time on the initial files. The serializing of content will delay the rest of the files that are needed until the DNS lookup has finished. A modern browser however can do multiple things at a time; unfortunately same-domain DNS lookups are not one of them. So while a user is waiting for each DNS lookup to occur, any files on that DNS are not downloaded until it has finished getting the IP from the DNS. I’m unfortunately using two different CDNs which contributes to the problem (2X the DNS lookups).

[caption id=”attachment_572” align=”aligncenter” width=”500”]DNS lookup times are no more; but now you aren DNS lookup times are no more; but now you aren’t using a CDN.[/caption]

When not using a CDN, there are no DNS lookup times for critical files and the server will make a connection to them quicker. This correlates to a faster initial page load because the critical assets have loaded quicker.

So what can be done? I’m not advocating to move critical assets off of a CDN entirely. CDNs still provide a much needed thing; geolocation to a user, static file caching and extra bandwidth for loading a website. A sites’ servers could be anywhere, and if they are halfway around the world from the user, the user will have a bad page load time.

Instead, a site should push critical assets with the page content and then dynamically cache them for later pages that use the CDN. Not all pages should do this. Only the pages that are the normal landing pages for users. Typically the ‘index’ of the site (e.g. www.google.com is usually hit before https://www.google.com/#q=) is the first a user will load. So front-load the critical assets on this page and prefetch them after the page has loaded. Then on a subsequent page, use the CDN like it is advertised. The files will already be cached; and if not a CDN will be there for fallback.

[caption id=”attachment_573” align=”aligncenter” width=”500”]The page now loads twice as fast. The page now loads twice as fast.[/caption]

To compare, my example now looks like this. A significant (~40%) time decrease by placing the jQuery and Normalize + global assets onto the page content and pushed with the first request. For a first time user this is a noticeable speed difference. To make sure that further pages to not accrue the DNS lookup time, or asset transfer times, prefetch the files you need.

1
2
3
4
5
$body = $('body');
// cache the files from the CDN
// a simple example for making subsequent pages faster
$('<img src="http://cdn.jsdelivr.net/normalize/3.0.0/normalize.min.css">').appendTo($body);
$('<img src="http://code.jquery.com/jquery-2.1.0.min.js">').appendTo($body);

You can also try to take advantage of the [prefetch](http://www.whatwg.org/specs/web-apps/current-work/#link-type-prefetch "the spec")[2][3][4] link tags for the CDN and assets. However, in real world use, these rarely seem to work well (too many variables will abort them like a document.write or more JS loading/rendering). I’ve found that caching via the <img> example works well and across all browsers (It’s recommended to use prerender links sparingly to limit SEO issue of double loading content for a non-view).

1
2
3
4
5
<link rel="dns-prefetch" href="http://cdn.jsdelivr.net/">
<link rel="dns-prefetch" href="http://code.jquery.com/">

<link rel="prefetch" href="http://cdn.jsdelivr.net/normalize/3.0.0/normalize.min.css">
<link rel="prefetch" href="http://code.jquery.com/jquery-2.1.0.min.js">

If you really want to be advanced, you can also prefetch your images; which most of the time are the worst offenders for page load speeds. Continue speeding the web forward! [Also, if you were curious, this own site has it’s own issue with performance tweaks. No worries, I’ve been working on a re-design that will remove the irony]