Three seconds. Past that mark, 53% of mobile visits are abandoned. That is Google's number, and thousands of developers fight it daily.
The reflex is to ship less JavaScript. The reflex has no target. Every speed fix lands on one step of one lifecycle, and you cannot optimize a step you cannot name. So name them first.
The round trip
Every website runs the same trip. At least one client, one server, and a myriad of infrastructure proxies. This post skips the basics of the Internet and HTTP requests. In short:
- The client, a browser, requests access to a website.
- The request crosses the infra and reaches a server.
- The server processes the request and returns one single HTML file. No JavaScript or CSS whatsoever.
- The client receives the payload and starts unpacking.
- Parse the HTML.
- Fetch other assets linked in the HTML file: JavaScript files, CSS stylesheets, images.
- Parse and evaluate JavaScript.
- The website is ready to be used.
- Maybe keep loading more resources.
VISUAL // ARCHIVEbrowser lifecycle resume640×409px
Ready has no single definition
Ready depends on the requirement. HTML ready? JavaScript ready? All the content? Interactive? Here is the client lifecycle:
- Download the markup.
- Construct the Document Object Model (DOM).
- Construct the CSS object model (CSSOM).
- Construct the Render tree.
- Calculate the Layout.
- Start the JavaScript blocking thread.
- Download the files.
- Parse, evaluate and execute it. More bytes, more time.
- Allocate memory. Fairly quick, and worth a mention.
- The browser fully loaded the HTML, and the DOM tree is built. External resources like pictures
<img>and stylesheets MAY not yet have loaded. - First contentful paint happens.
- Everything is loaded.
- The page is interactive. It is completely ready.
- Other deferred requests finish and run later.
That is the theory. The browser exposes states you can hook. Four of them matter.
An inline script has no DOM to touch
It runs at the end of step 6 of the client lifecycle, right when the JavaScript first executes. Use it for JavaScript with no DOM relationship.
<head>
<script>
console.log('lol')
</script>
</head>
<p>pi, also known as "exactly 3.14" for engineers</p>Position changes the timing. A script in the head does not behave like one at the end of the body, or one with defer, or one with async.
DOMContentLoaded is the hook 9 times out of 10
It fires at step 7. It is the main browser event, and it marks the readiness for any kind of JavaScript to start working. This is the earliest safe hook. Everything that needs to be ready is ready. It got popular through jQuery. Yes, jQuery.
document.addEventListener("DOMContentLoaded", function() {
// your marvelous code here
})load waits for the images
It fires at the beginning of step 10. All the assets are loaded, the page has everything ready, and the users can interact with it. Use it for JavaScript that needs the assets in place, images included.
// window.onload is the same
window.addEventListener('load', () => {
// here you can access anything in the page, and everything is ready
}readyState answers at any time and never calls back
Call it whenever you want. It gives you different results and it is not reactive. So you have to keep calling it. Use it for later check outs, or for browsers that do not support the other hooks. Bonus tip: drop their support.
const checkBrowserStatus = () => {
if (document.readyState == 'loading') {
// still loading
} else if (document.readyState === 'interactive') {
// DOM is ready!
} else {
// everything is ready!
}
}The same property covers the legacy path. Does your employer force you to drink sparkling water and support IE8? Here is the alternative magic.
// alternative to DOMContentLoaded
document.onreadystatechange = function () {
if (document.readyState == "interactive") {
// look for another job
}
}SSR does not change the client lifecycle
There is a lot of confusion around this topic, and the answer is almost nothing. Any SSR or static component rehydrates a view. It feeds the server request to a function. The function returns a string as the HTML markup for the webpage. That HTML might unfold later in the client into a fully-featured SPA. That is common. It is optional. SSR is a more complex step 3 of the round trip.
One constraint survives. The server is not the browser. The client API is not available there. So no window checking, no setting hooks, no hack you have on your mind. Wait for client execution to orchestrate that work.
Profile before you tune
- Profile your product first. The problem could be in many different places.
- Set one goal: a rapid interactive time. It drives everything else towards the same destination.
- Defer everything else you do not need.
Three seconds. You get to spend them once.
