While redesigning my site, I wanted to see how performant I could possibly make it, whilst retaining a clean and elegant style. The most important aspect to achieve this turned out to be auditing the third-party scripts I was using, and modifying how I loaded them into the page.
Establishing a baseline
One of the main timing metrics I used was DOMContentLoaded
, the time it takes for the page to be loaded and parsed not including stylesheets, scripts or other media. I picked this as my measurement because it shows how much the loading and executing of scripts is blocking the parsing of HTML in the document and therefore adding to page load times.
To start testing, I began with a simple page that has a header, 3 paragraphs of text, a google maps iframe and a footer. To make sure that caching wasn’t interfering with the results I disabled it, so every refresh was a fresh new load of all resources. Without optimization, DOMContentLoaded was averaging 672ms
over 10 runs.
Measuring the improvement.
After carefully considering where to use async
and where to use defer
, I made the modifications and saw an immediate improvement: a load time average of 241ms
after 10 runs.
before | after |
---|---|
672ms |
241ms |
I only added attributes to 4 scripts, with 1 × async
and 3 × defer
, but the results were night and day, almost 2.8x faster load times. This is for a page that only weighed 845KB
, so I imagine there would be significant blocking, given the use of larger scripts and larger pages.
async
...
<head>
<!-- using async -->
<script async src="https://..."></script>
</head>
...
The async
attribute instructs the browser to continue parsing the rest of the document while the script loads. It will execute as soon as the script has loaded and then go back to parsing if there is more to be done. In real terms then, the order of execution is determined somewhat by filesize and network conditions.
This is super useful for third-party scripts that you know have no dependency on DOM content, as in it won’t be make or break depending on what’s on your page. Because of the asynchronous nature of loading, it also makes the execution order a roll of the dice, so using async
for anything that isn’t mission-critical is likely to be a winner.
defer
...
<head>
<!-- using defer -->
<script defer src="https://..."></script>
</head>
...
defer
tells the browser load the script while continuing to parse the document, deferring (delaying) execution until the whole document has been loaded and then going back and executing each deferred script in order of inclusion.
This is useful for preventing scripts with dependencies from blocking page load, yet maintaining a desired order of execution. Certainly be sure to use defer
if you have written any scripts that rely on DOM elements existing as this attribute will ensure that they are there (unless your elements are coming from an XMLHttpRequest).
Browser Support
Support is excellent for both techniques, with modern browsers handling them as part of the HTML5 specification. You can see a summary of browser support on caniuse.com for async and defer respectively.
Conclusion
Using async
and defer
are trivially easy to add to a website but if used correctly, significantly help improve load times. Hopefully this will be another feather in your cap for the next time you are trying to improve page performance in your own web applications.