I got this from Nate Berkopec on the parley.rubyrogues.com forum:
I know nothing about React, can you clarify a few statements?
If the head script includes “defer” in the application.js (which it should!), then we need to include defer in the body scripts!
What head scripts and body scripts are you referring to in the react_on_rails gem?
If I understand your question correctly, if someone is using defer
on their application.js
, you think your React script tags should also use defer
so that they will be executed after application.js
. Unfortunately, this won’t work for you, for two reasons.
Incorrect and not-always-available implementations: defer
is dangerous - it isn’t implemented across all browsers, and some browsers have buggy implementations (IE9) that don’t work exactly correctly.
defer
’s promise is that a script tag with defer
set will execute after the document has finished parsing. Again, however, this not entirely guaranteed across all browsers. A real danger here is that you might assume that these scripts will execute in order, that is, a script tag with defer
that comes before another one in the document will be executed first. This cannot be assumed, even though this is how you would think it would work in the spec, because this behavior was implemented incorrectly by IE9 (and possibly other browsers).
Defer doesn’t work with inline scripts. defer
and async
are typically used for script tags with a src
attribute (i.e., not inline), and MDN says that Firefox will ignore defer on scripts without a src attribute. In fact, re-reading the HTML spec now, I notice the following: “The defer and async attributes must not be specified if the src attribute is not present.” So inline script will always be executed before a defer
red external script.
Hopefully that answers your question for your particular project.
If you’re purely looking for a performance gain - that is, to speed up page rendering by unblocking the document parser, it is far better to be looking at async
. Async
unblocks the document parser (as explained in your links), but will also execute the script as soon as it is ready. You may be thinking: “Well, that script might be ready before the document is finished parsing!” That’s correct - async
may still cause execution while the document is parsing, delaying load. However, since async
makes no guarantees about when the script will be executed (which is really what defer
was doing all along, although it told you something different!), I think it’s better for us to recommend it’s use over defer.
Finally, the linked StackOverflow answer also does not mention the browser preloader, something we’ve had in all major browsers for a few years now. What the answer fails to mention is that at step 4 (when the browser parser stops while a script downloads), a speculative lookahead parser called the browser preloader is sent ahead in the document to look for resources and download them. This makes a lot of the discussion regarding “where in the document a script tag should go” mostly irrelevant for modern browsers, especially when that script tag has async or defer set. Here’s link to my blog about browser preloaders and other ways to prefetch resources2.
Follow-up response from Nate:
Both async and defer will do absolutely nothing if a script tag does not have a “src” attribute. Scripts without “src” attributes (i.e inline or not external) will always be executed straight away by the parser as soon as they are encountered. Anywhere else, I prefer using “async” along with “defer”. If a browser doesn’t support “async”, it will fallback to “defer”. All of this is behavior straight from the HTML Standard and implemented consistently across browsers.