In an age not too long and not too far ago, the main characteristic used to assess web page performance and front end optimization was page load times. Most sites were built with images, HTML and some CSS.
Fast forward to today, web pages have evolved to be much more complicated and rich. In today’s reality, web pages may still download for less than one second but render for much longer.
From the user’s perspective, the sole consideration is that she sees something meaningful on the page in a relatively short time span. That means, we need to shift our focus to page render time and invest effort in reducing the time to first paint.
A typical HTML page developed today might look like this:
2. Query the DOM for Select Style Properties as Needed
To access this information, we will need first the CSSOM to be constructed. While building the DOM tree is incremental, the CSSOM tree must be completed at once. It can’t simply apply styles incrementally. Why? We will get a rather disturbing shift of elements on the page.
Remember: We can’t see anything on the page until the CSSOM is done. Otherwise, we might experience a flash of unstyled content (FOUC) shown in the graph below.
Now we know why there is a problem and what we need to achieve front end optimization. Here’s how to execute on it.
3. Don’t Include the Entire External Library CSS in the <Head> Section of Your Site
Why load the bootstrap theme CSS and a bunch of extra CSS files up in front if nobody will actually see those styles when first loading the page? Because, CSS is render blocking, large stylesheets up in front will only cause slow CSSOM creation — therefore causing slow time to first paint.
The basic approach to cope with this issue is to simply extract the critical CSS for the “above the fold” content of your pages. This can easily be done by Node.js based tools like the gulp task critical.
It just takes a simple configuration based on which will load up your site under different resolutions, then check which CSS is used for the visible areas on them.
The output will be just the CSS you need for the dimensions and urls provided. As a bonus, it can also minify it.
Finally, simply inline. (We do not need an extra request for a small CSS file. We can just put in <style> </style >) that minified CSS as high as possible in your web page. This will enable the browser to render as soon as enough as the CSSOM is constructed and enough DOM is available — since it is built incrementally.
All the other necessary resources will be loaded asynchronously so we avoid render blocks.
4. Control What Blocks Your Page Renderer, and What Does Not by Preloading and Preemptively Caching Needed Assets
All external CSS resources immediately download once they are reached by the parser. Adding a non-existent media type attribute on the link element will cause it to download, but not render, so it won’t block.
A library has already been developed enabling this programmatically.
Impact of New Rel= Attribute Values
Beyond this, a fairly new concept has already taken place in the newest browser iterations, and it looks like it is here to stay. A new set of rel attribute values of the link tag allows the preloading of resources.
One of the newest is rel=“preload”. It enables resources to be loaded to pre-empt what is needed in the future and cache them. As an added benefit, it doesn’t block the window’s onload event either.
I mention rel=“preload” at all, because it can also be used to effectively load page resources.
<link rel="preload" as="style" href="async_style.css" onload="this.rel='stylesheet'">
Here’s How It Works…
Using the command, the browser downloads the resource. Adding the as attribute tells the browser what it is fetching, and thus it gives it a higher priority.
<link rel="preload" as="script" href="async_script.js" onload="var script = document.createElement('script'); script.src = this.href; document.body.appendChild(script);">
For many, the question becomes, “Why bother with this technique we can just use the seemingly :<script async> magic to achieve front end optimization?
Yes, while we can use async, it blocks the window onload event, which is a marker we may need to load other resources.
Specify the Resource We Want to Dynamically Download
Also, another handy feature of using preloaders is that we can actually take use of the media attribute of link and specify which resources we need downloaded under specific resolutions or device types.
<link rel="preload" as="image" href="map.png" media="(max-width: 600px)">
<link rel="preload" as="script" href="map.js" media="(min-width: 601px)">
Remember: These are not immediately executed/parsed. They are just preloaded. They are still taking bandwidth and a TCP connection though, so we have to acknowledge that in our critical rendering path too. To balance this, we can do an “in-advance dynamic loading without execution.” While it sounds complicated, it’s a fairly efficient method to advance front end optimization.
var preload = document.createElement("link"); link.href = "myscript.js"; link.rel = "preload"; link.as = "script"; document.head.appendChild(link);
The script above will just preload the resource dynamically. To use it, just insert it:
var script = document.createElement("script"); script.src = "myscript.js"; document.body.appendChild(script);
In applying this piece of code, imagine that you are building a web multi-screen single-page application. You don’t need all the resources for all the screens — just the ones for the homepage. That said, you want to still be able to navigate to the other pages quickly. Therefore, the resources must be loaded. Of course, you can use dynamic scripts loading, but that lead to lag time on a blank page, while the scripts are downloading. (Remember Require.js?)
This is exactly where the benefits of dynamically preloading content become clear. Just think of the user’s flow in your app and dynamically preload the resources required for the next screen.
For example: Profile Screen -> Edit Profile Screen
5. Combine Resources into Single Resource Type Files
If our application is a large one, say we have more than 20 script and CSS file dependencies, we might benefit from combining all those into single resource type files.
Chrome, for example, can only open 6 parallel TCP connections to a domain, which would mean that if we really have 20 resource dependencies on the same server, we would have to download most of them sequentially — an obvious detriment to load times. (This is also true for image resources.)
With the help of widely-spread Node.js packages like gulp concat, we can easily combine all those resources into one, and then, of course, minify it for even less load time.
We can still extract our critical resources and load all the rest after the onload event for example. That allows us to go from this bad waterfall:
To this one:
Though, be advised for projects where lots of iteration on the same code base are taken to production, this might not be the best tactic, since you will cache-bust the whole bundle, for every single change on any of the files in it, no matter how small.
Another good approach to cope with this would be to concatenate all your third party dependencies in a separate bundle, or even figure out your own “mixed” bundle with components that you are certain are not going to change often.
Just using these 5 tips, you will start to see large front end optimization gains in your web project.