Web Performance for Dummies

  • Ju Liu
  • Aug 29, 2016
Engineering

The title of this article should be ‘Web Performance for Someone Who Has Developed Applications For Quite a Long Time But Still Has No Idea Really’. That would be me a couple of days ago.

I realised I had no idea how a browser worked. My general understanding was something along these lines:

  • it sent a network request to a server
  • it received some HTML in response
  • it parsed the HTML to fetch images, CSS and Javascript
  • it parsed the CSS and JS and ran them in some order
  • it rendered the page

If that’s all you know about a browser, don’t feel bad and keep reading. You’re in for a treat.

The Critical Rendering Path

What we mean with critical rendering path is the process that the browser goes through to render what is absolutely needed on the page. Making our critical rendering path as short as possible is crucial to start showing content to our users.

So let’s start with a very simple example. We create a new folder with a file called ‘index.html’:

Then we open the terminal and we start a simple HTTP server:

python -m SimpleHTTPServer

You should see some output like this:

Serving HTTP on 0.0.0.0 port 8000 ...

Now if you visit http://localhost:8000 in your browser, this amazing series of events will unfold:

  • your browser will ask the HTTP server for a file called ‘index.html’
  • the python HTTP server will find the file and start transmitting it
  • when the browser has received the file, it will start parsing it

When I say parsing it, I actually mean:

  • converting the bytes to characters
  • converting the characters to tokens
  • converting the tokens to nodes
  • linking the nodes together to form the DOM

Here’s the whole process in its glorious beauty:

DOM you said?!

If you are a Real Web Developer® you’ve heard about the DOM already. It stands for Document Object Model and it represents the browser interpretation of the HTML document.

Every web page that you ever visited had to be converted from its binary representation to a DOM. Computer scientists will go crazy about the DOM because it’s a tree. Just keep ignoring them.

The beauty of this conversion process is that it generally goes step by step (browser try to cheat and be smarter): so in our example the parser will encounter ‘html’, ‘head’, ‘meta’ and then it will hit the ‘link’ tag. At this point, the browser will request the ‘style.css’ file from the server. Here it is:

This file will undergo a very similar parsing process, which will produce something I’ve never heard before until this week: the CSSOM. Look at this amazing thing, it’s another tree!

As you all know CSS stands for Cascading Style Sheets, but no web developer can agree on why it’s called Cascading in the first place. My theory is that the rules apply in a cascading fashion from the top to the bottom. Let me know in how many ways I am wrong.

The nice thing is that when the CSSOM has been built, we know for each element in the page how it should look like.

Note that the above tree is missing the default styles that every browser ships out of the box. If you want to see the real styles, right click, choose ‘Inspect’ and then on the right-hand side of the inspector choose the ‘Computed’ tab. You should see something like this:

Ok, so now we have the DOM and the CSSOM. What’s next?

In order to understand what to show on the page, the browser combines the DOM and the CSSOM in a single tree, called the render tree. This tree will only contain the tags that can be displayed on the page, so for example ‘meta’, ‘script’ tags won’t be included. Another interesting tidbit is that tags which are set to “display: none” will be excluded too!

Let’s see how the render tree might look like:

As we can see the ‘body p span’ tag has been excluded in the final tree. Also we can appreciate that the DOM contains the contents of the page and CSSOM contains how the page should look like, but only the render tree contains all the information that we need to render the page.

From the render tree, the browser can start to understand where to position elements in the page and calculate their dimensions in a phase which is generally known as Layout.

When that has been done, it is finally ready to draw the actual pixels on your screen. This process is called Paint.

And there you have it! If you don’t believe me (you shouldn’t), you can open your DevTools, choose the Timeline tab and refresh. You’ll see something like this:

You can see that all the steps that we have described are actually there. Isn’t that awesome? Now, let’s move on to…

The Only Thing I Want You To Remember

If there was a single thing I want you, the reader, to take away from this article is the following:

By default CSS is treated as a render blocking resource, which means that the browser will hold rendering of any processed content until the CSSOM is constructed.

What does this mean?

Browser: “I won’t display anything on the page before the CSSOM is built.”

Why does this happen?

Browser: “Oy mate, if I start rendering the page without waiting for the CSSOM, the page won’t have styles, then the CSSOM will be ready, then the styles will kick in, the page will flash and bad things will happen…”

Uh, ok.

So is there something we can do to speed things up?

Media Types and Queries to the Rescue

Let’s say we have a website that sells the best cupcakes that money can buy. Since we are nice people, we provide a mobile stylesheet so that our customers can buy cupcakes from their phones. Since we now know that CSS is render blocking, we don’t want to inconvenience our desktop users with this additional CSS. So what can we do?

By passing a ‘media’ attribute to our link tag, we can tell the browser that the mobile CSS isn’t render blocking if the device’s maximum width is less than 480 pixels.

The same principle applies to the styles we want to apply to the page for other purposes, such as printing (who is still printing web pages though??). In that case you can use the ‘type’ attribute:

Note that the additional stylesheets will still be downloaded by the browser, but they won’t be blocking rendering anymore!

Let’s move on. There is something we still haven’t mentioned.

The Elephant in the Room

A picture is worth a thousand words, they say.

So what about JavaScript? Let’s take a look at this:

When the browser is parsing the DOM, it encounters the ‘script’ tag and has to pause, run the script, and only when that’s done it can resume parsing the DOM.

Inline scripts block DOM parsing and therefore block rendering. They’re evil.

What if we move the script to an external file?

In that case the browser will pause DOM parsing and start fetching the script: only when that’s finished, it will run the script and resume parsing the DOM. What a waste!

So do you remember when I told I wanted you to remember only one thing from this article? Well, I lied.

The Second Thing I Want You To Remember

The browser will delay script execution until it has finished downloading and constructing the CSSOM, and while we’re waiting, the DOM construction is also blocked.

Let’s ask our friend:

Browser: “Hey man, I can’t really run scripts before the CSSOM has been built, cause the script could be reading or modifying it.”

But what if we had scripts which don’t really need to load during DOM parsing?

Async and Defer

These are attributes that can be passed to the script tags to change how the browser handling script loading and handling.

So here’s how a normal script is loaded and executed:

Blue -> Script Loading; Red -> Script Execution

If we use ‘async’ here how this behaviour changes:

Loading is deferred, but script execution still blocks rendering

And here is what the ‘defer’ behaviour:

Nothing is blocked!

A great use case for ‘defer’ are JS analytics scripts. Read more about these attributes here.

The Mantra of Performance Optimisation

You can’t optimise what you can’t measure. You can’t optimise what you can’t measure. You can’t optimise what you can’t measure. You can’t optimise what you can’t measure. You can’t optimise what you can’t measure. You can’t optimise what you can’t measure. You can’t optimise what you can’t measure.

Luckily we can use the Chrome DevTools to trace what the browser is doing and see something like this:

Dat flame-graphs

And to Conclude, Some Fun Exercises!

So I’ve made a repo on github which contains different versions of a simple web page, so that you can play with the timeline yourself!

Clone the repository and check out all the different tags and get your optimisation juices flowing :)

Thanks for reading!

Amazing Resources I’ve Stolen Stuff From

 

Some Slides If You Are That Type of Person


Ju Liu joined AlphaSights in March 2014 and works as a Lead Software Engineer in our London Office.