5 ms·
There's a lot of good advice here, but also some misinformation. First of all, a script tag will block DOM construction, but in any sane modern browser will no
by bzbarsky 11y ago
There's a lot of good advice here, but also some misinformation.
First of all, a script tag will block DOM construction, but in any sane modern browser will not block loading of subresources like stylesheets, because browsers speculatively parse the HTML and kick off those loads even while they're waiting for the script. So the advice to put CSS before JS is not necessarily good advice. In fact, if your script is not async it's actively _bad_ advice because while the CSS load will start even before the JS has loaded, the JS will NOT run until the CSS has loaded. So if you put your CSS link before your JS link and the JS is not async, the running of the JS will be blocked on the load of the CSS. If you revers the order, then the JS will run as soon as it's loaded, and the CSS will be loading in parallel with all of that anyway.
Second, making your script async will help with some things (like DOMContentLoaded firing earlier and perhaps getting something up in front of the user), but can hurt with other things (time to load event firing and getting the things the user will actually see up), because it can cause the browser to lay out the page once, and then lay it out _again_ when the script runs and messes with the DOM. So whether it makes sense to make a script async really depends on what the script does. If it's just loading a bunch of not-really-used library code, that's one thing, but if it modifies the page content that's a very different situation.
Third, the last bullet point about using DomContentLoaded instead of $(document).ready() makes no sense, at least for jQuery. jQuery fires $(document).ready() stuff off the DomContentLoaded event.
The key thing for making pages faster from my point of view is somewhat hidden in the article, but it's this:
> This isn't even that much JavaScript in web terms - 37kb gzipped.
Just send less script. A lot less. The less script you're sending, the less likely it is that your script is doing something dumb to make things slow.
[Disclaimer: I'm a browser engine developer, not a frontend or full-stack web developer.]
- nateberkopec 11y ago> In fact, if your script is not async it's actively _bad_ advice because while the CSS load will start even before the JS has loaded, the JS will NOT run until the CSS has loaded. If you revers the order, then the JS will run as soon as it's loaded, and the CSS will be loading in parallel with all of that anyway. Scripts without an async attribute will cause the browser to stop where it's at in the DOM and wait for the external script to download. The browser will not start downloading any stylesheets that come after the script tag until that's done. This is all straight from Google and can be verified in Chrome Timeline. https://developers.google.com/speed/docs/insights/BlockingJS https://developers.google.com/speed/docs/insights/BlockingJS > So if you put your CSS link before your JS link and the JS is not async, the running of the JS will be blocked on the load of the CSS. Downloading CSS isn't required to fire DomContentLoaded, so most browsers will download it in a non-blocking fashion. Chrome does not behave in the way in which you describe, which is shown plainly in the article. Again, this can be verified in Chrome Timeline. All that said, I'm curious as to what browser engine you work when where what you said is the case - I don't doubt it, but you're not describing the way Chrome (and, I suspect, Webkit) works. Right-o on the bit about DomContentLoaded, will fix.
- evmar 11y agoThere are two kinds of blocking that are easy to confuse. One is network: whether the browser will wait for a resource to fetch before fetching another. The other is execution: whether the browser will wait for a resource to execute (which itself depends on fetching) before executing the next. Script async controls the latter. WebKit (and consequently Chrome) have a "preload scanner" (you can Google for those words to find posts about it) that attempt to parallelize the former in all cases. That is to say, a <script> followed by a stylesheet should always fetch both in parallel. The "async" tag controls whether the browser waits for the script to finish loading and executing before rendering the rest of the page. I think this is a relevant snippet of Chrome code: https://code.google.com/p/chromium/codesearch#chromium/src/third_party/WebKit/Source/core/html/parser/HTMLPreloadScanner.cpp&q=preloadscanner&sq=package:chromium&l=364 https://code.google.com/p/chromium/codesearch#chromium/src/t... That's from the preload scanner, where it's deciding whether to fetch a URL referenced on the page. As far as I understand it this runs in parallel with any script execution blocking etc. (Disclaimer: I worked on Chrome but never on this part.)
- youngtaff 11y agoIt's a couple years old but this is a pretty good explanation of the preloader - http://andydavies.me/blog/2013/10/22/how-the-browser-pre-loader-makes-pages-load-faster/ http://andydavies.me/blog/2013/10/22/how-the-browser-pre-loa...
- paulirish 11y ago(Mostly agreeing, just adding some more)... I suppose theres 3 kinds of blocking really: 1. Network blocking. Requests must wait until the previous one finished. Browsers in 2005 might have done this but not anymore. Even in terrible document.write() scenarios, browsers will still try to do additional work/requests. 2. JS Execution blocking. The difference between [async] and [defer]. Browsers default to executing JS in order, which sucks if an early script takes forever to download. And layout/rendering is typically awaiting all this script to execute anyway. 3. Render blocking. (Or technically, layout blocking). Can the browser try to display pixels before this script has finished downloading or executing? By default, it cannot, but an [async] attribute at least allows the browser to. #2 and #3 definitely matter, with render blocking behavior usually being the most important. [async] and decreasing script request count are very good.
- seanwilson 11y ago> Just send less script. A lot less. The less script you're sending, the less likely it is that your script is doing something dumb to make things slow. Isn't it more important to benchmark the parsing and execution time of your script? A large well optimised script could execute faster than a small unoptimised script.
- gsnedders 11y agoIn the majority of cases, load time dominates over execution time.
- bzbarsky 11y agoIf you're going to benchmark (especially on a variety of network connections), that trumps everything else, of course. Measurement of the actual workload is pretty much always better than general discussion about how workloads should be structured.
- gcb0 11y ago> [Disclaimer: I'm a browser engine developer, not a frontend or full-stack web developer.] that is why you have a sane view :) on the wed dev trenches, more is better. we are all insane ppl who think adding react to a page will make it spiffier.
- at-fates-hands 11y ago>> Just send less script. A lot less. The less script you're sending, the less likely it is that your script is doing something dumb to make things slow. This - a thousand times. This is why I tend to shy away from CMS' like Wordpress and Drupal. The amount of JS they load is insane. Not only do they load JS directly in the <head>, but then load a bunch more at the bottom of the page. The problem with these is you either download and configure an easy to use plugin every time you need some functionality (image gallery, contact form, meta tags for seo) which load their own scripts and css stylesheet, or you have to write your own and then integrate into whatever CMS you're using. two good Wordpress examples: http://worryfreelabs.com/ http://worryfreelabs.com/ - 6 scripts in the head, more than a dozen after the <body> tag. 113 requests and a staggering 14.7MB just on the home page. http://www.thisisyourkingdom.co.uk/ http://www.thisisyourkingdom.co.uk/ - 9 scripts in head, 4 more in the body and close to a dozen style sheets that were loaded. 68 requests which took close to 9 seconds to load.
- fabian2k 11y agoAt least Drupal 8 is much better about this, by default it doesn't load any JS unless there is actually anything on the page that requires it. And scripts are added below the main content and not in head. Caching also got much better in Drupal 8 and is enabled by default, which makes a huge difference in speed. You always have to be careful with a CMS, it's very easy to load a lot of unnecessary stuff, but you can still build very fast and lean sites with Drupal with only a bit of knowledge.
- at-fates-hands 11y agoI've been thinking of going back to Drupal for some time now. Thanks for the heads up to take another look at it again.
- luxpir 11y agoI'll throw in my own 2 cents, in agreement. I have tried to use SPDY, Nginx 'microcaching', Drupal caching to make my own Drupal 7 project load as quickly as possible. I still need to make the font and bootstrap calls local (potentially dropping the fancy font for Georgia or Verdana, even Helvetica...) to really bring the load times down further. There are other settings to tweak still, but it's one example of Drupal not dragging its feet too much. It certainly let this 'dev' create a usable piece of software, at least. > https://www.linguaquote.com https://www.linguaquote.com PS - also looking forward to Let's Encrypt and http2 in Nginx.
- paulirish 11y ago> making your script async … can hurt with other things I can't agree. It will likely regress window.load time, but that's a bad metric. Additionally, the reflow cost post-execute will be <100ms, but the delay to the paint caused by a sync script worth 100-5000ms. In very rare occasions would it be preferable to show a white page to a user longer to avoid the DOM change costs. > DomContentLoaded Agree with the above. Additionally, I believe all browsers wait for DCL handlers to finish loading before painting (for the common case). But, most pages have ALOT of code attached to DCL. If the handlers are not critical for the first paint (they shouldn't be), then the easy fix would probably be document.on('DOMContentLoaded', e => { requestAnimationFrame(function(){ /* your doc.ready stuff */ })}); That way you get a paint to the user before your piles of DCL code runs.
- taf2 11y agoI think this is what https://github.com/wilsonpage/fastdom https://github.com/wilsonpage/fastdom was designed to help with... The issue I have with it is it's another library to load...
- bzbarsky 11y ago> Additionally, the reflow cost post-execute will be <100ms It really depends on the page. It's pretty easy to come up with pages that take a lot longer than that to reflow (tables, deeply nested flexboxes, etc). I agree that typically making the script async is a good idea, but it's really worth measuring both ways for the particular script and page in question.
- gsnedders 11y ago> First of all, a script tag will block DOM construction, but in any sane modern browser will not block loading of subresources like stylesheets, because browsers speculatively parse the HTML and kick off those loads even while they're waiting for the script. And some don't just preload resources from the speculative parsing—unless invalidated, the speculative parse is the tokenizer stage (into things like start tags, end tags, comments, characters, etc.) of the parser, and they're fed directly into the DOM construction once unblocked. > > This isn't even that much JavaScript in web terms - 37kb gzipped. > Just send less script. A lot less. The less script you're sending, the less likely it is that your script is doing something dumb to make things slow. This. Seriously this. The amount of bloat in JS is insane—it's almost always the case that a lot less could be sent without any loss of functionality nowadays, and that's really, really sad.
- samstave 11y ago>[Disclaimer: I'm a browser engine developer, not a frontend or full-stack web developer.] I read that in McCoy's voice: "Dammit Jim, I'm a browser engine developer, no a front-end or full-stack web dev!"