In a discussion around optimizing front-end assets, I recently had occasion to
explain how browsers process
<script> tags — which seemed useful enough to be
In order to assess the consequences of any such decision, it helps to
understand how browsers work: When the browser processes an HTML document, it
does so from top to bottom. Upon encountering a
<script> tag, it halts (“blocks”)
further processing2 in order to download the referenced script file.
processed, HTML processing continues.
Let’s imagine the following document:
The browser might actually begin rendering the page even before it has fully
downloaded the HTML file. Thus you might see the browser window reading “Hello
World” (thanks to the
<title> tag) while the page is still blank.
Once we arrive at
<script src="foo.js">, processing halts as described above.
Afterwards, we continue to
<script src="bar.js">, repeat the same procedure,
and then move on to
<script src="baz.js"> for the final piece. That leaves us
with the following sequence:
Concatenation would mean combining these files into a single one:
While the amount of content transferred remains identical3, this is generally faster because there’s less networking overhead. (Obviously I’m simplifying a bit here.)
Browsers these days support this with the simple addition of
a dedicated attribute:
<script src="…" defer></script> (in fact, there’s also
another, similar attribute:
except this one doesn’t guarantee order of execution; see
details). However, this doesn’t work for inline scripts (of which,
unfortunately, there were a few in the project at hand), so those would likely
execute before the deferred external scripts they depend on become available.
Now, you might argue that HTTP/2 makes all of this a non-issue because it reduces protocol overhead — but in fact, even HTTP/2 is still prone to the laws of physics:
As described above,
<script> tags are processed sequentially — which means
that the browser doesn’t know it should retrieve
bar.js until after
has been fully loaded. Thus it actually has to wait before even requesting
that file from the server:
Depending on connectivity, that latency can be significant.
However, if we were using
<script> tags would be non-blocking,
which means the browser could request both files simultaneously:
This is why concatenation can actually be a net negative with HTTP/2, as it prevents parallel downloads:
Network protocols aside, it’s generally good practice to relegate script tags
to the bottom in order to avoid unnecessarily blocking static HTML content. In
the example above, even if the entire HTML document has already been
bar.js are slow to load (for which there are
myriad potential reasons),
they’d prevent the content below from being displayed.
i.e. instead of serving source files individually, combining them into a single file for distribution ↩
for somewhat arcane historical reasons related to
minification, by contrast, reduces the amount of content (e.g. by removing whitespace that's only relevant for us puny humans) ↩