When loading a script on an HTML page, you need to be careful not to harm the loading performance of the page. Depending on where and how you add your scripts to an HTML page will influence the loading time.
将脚本添加到HTML页面的位置和方式将影响加载时间,所以,加载脚本时,需要注意不要影响到页面的加载性能。
When loading a script on an HTML page, you need to be careful not to harm the loading performance of the page.
在HTML页面中加载脚本时,需要注意不要影响到页面的加载性能。
A script is traditionally included in the page in this way:
通常使用这种方式在页面中添加脚本:1
<script src="script.js"></script>
whenever the HTML parser finds this line, a request will be made to fetch the script, and the script is executed.
每当HTML解析器找到该行时,将发出一个获取脚本的请求,然后执行脚本。
Once this process is done, the parsing can resume, and the rest of the HTML can be analyzed.
完成此进程后就继续解析HTML的其余部分。
As you can imagine, this operation can have a huge impact on the loading time of the page.
可以想象,此操作可以会对页面加载时间产生巨大影响。
If the script takes a little longer to load than expected, for example if the network is a bit slow or if you’re on a mobile device and the connection is a bit sloppy, the visitor will likely see a blank page until the script is loaded and executed.
如果脚本加载的时间超出预期(比如网速慢或者使用手机连接不稳定),在脚本加载并执行结束前呈献给页面访问者的都是空白页面。
位置的重要性
When you first learn HTML, you’re told script tags live in the
tag:初学HTML一般在head标签中添加脚本,如下:
1 | <html> |
As I told earlier, when the parser finds this line, it goes to fetch the script and executes it. Then, after it’s done with this task, it goes on to parse the body.
前面提到,每当HTML解析器找到该行时,将发出一个获取脚本的请求并执行脚本。结束此任务后将继续解析页面中的body部分。
This is bad because there is a lot of delay introduced. A very common solution to this issue is to put the script tag to the bottom of the page, just before the closing tag.
这是一种很糟糕的做法,因为它会产生许多延迟。一个常见的解决方案是将script标签放到页面底部(也就是body结束标签前)。
Doing so, the script is loaded and executed after all the page is already parsed and loaded, which is a huge improvement over the head alternative.
这样,在解析和加载所有页面之后加载并执行脚本,是对头部替代方案的巨大改进。
This is the best thing you can do, if you need to support older browsers that do not support two relatively recent features of HTML: async and defer.
如果你需要支持不支持两种相对较新的HTML特性的旧浏览器,那么async(异步)和defer(延迟)是你的最佳解决方案。
Async 和 Defer
Both async and defer are boolean attributes. Their usage is similar:
async和defert都是类型为布尔值的属性,它们的用法很相似:1
<script async src="script.js"></script>
1 | <script defer src="script.js"></script> |
if you specify both, async takes precedence on modern browsers, while older browsers that support defer but not async will fallback to defer.
如果同时使用两个属性,async则在现代浏览器优先使用,而支持defer而不支持async的旧浏览器将退回为defer。
These attributes make only sense when using the script in the head portion of the page, and they are useless if you put the script in the body footer like we saw above.
这两种属性只在我们将script标签放在页面head标签中时起作用,放在页脚中是无效的。
性能对比
head中无async和defer时
Here’s how a page loads a script without neither defer or async, put in the head portion of the page:
下图为页面中没有defer或async,并且将脚本放在head标签中时加载页面的效果:
The parsing is paused until the script is fetched, and executed. Once this is done, parsing resumes.
页面解析在请求到脚本时暂停,然后执行脚本。执行结束后,解析继续执行。
body中无async和defer时
Here’s how a page loads a script without neither defer or async, put at the end of the body tag, just before it closes:
下图为页面中没有defer或async,并且将脚本放在body结束标签前时加载页面的效果:
The parsing is done without any pauses, and when it finishes, the script is fetched, and executed. Parsing is done before the script is even downloaded, so the page appears to the user way before the previous example.
解析在没有任何暂停的情况下完成时,获取脚本并执行。甚至在下载脚本之前就已经完成了解析,所以用户可以比前面的示例更早看到页面。
head中有async时
Here’s how a page loads a script with async, put in the head tag:
下图为将带有async属性的脚本标签放在head标签时页面加载的效果:
The script is fetched asynchronously, and when it’s ready the HTML parsing is paused to execute the script, then it’s resumed.
异步获取脚本,当它准备好执行脚本时,将暂停解析HTML,开始执行脚本,脚本执行结束后重新开始解析HTML。
heade中有defer时
Here’s how a page loads a script with defer, put in the head tag:
下图为将带有defer属性的脚本标签放在head标签时页面加载的效果:
The script is fetched asynchronously, and it’s executed only after the HTML parsing is done.
脚本通过异步方式获取,并且在HTML解析完成后才开始执行。
Parsing finishes just like when we put the script at the end of the body tag, but overall the script execution finishes well before, because the script has been downloaded in parallel with the HTML parsing.
解析完成就像我们将脚本放在body标记的末尾一样,但总体来说脚本执行再次之前完成更好,因为下载脚本与HTML解析并行进行。
So this is the winning solution in terms of speed 🏆
所以这就是速度方面的最佳解决方案 🏆
阻塞解析
async blocks the parsing of the page while defer does not.
async会阻塞页面解析,而defer不会。
阻塞渲染
Neither async nor defer guarantee anything on blocking rendering. This is up to you and your script (for example, making sure your scripts run after the onLoad) event.
async和defer都不保证阻止页面渲染。这取决于你和你的脚本(如:确保你的脚本在onLoad之后运行)事件。
domInteractive
Scripts marked defer are executed right after the domInteractive event, which happens after the HTML is loaded, parsed and the DOM is built.
标记为defer的脚本在domInteractive事件之后立即执行,这发生在加载、解析完成HTML并且构建DOM之后。
CSS and images at this point are still to be parsed and loaded.
此时的CSS和图像仍然需要解析和加载。
Once this is done, the browser will emit the domComplete event, and then onLoad.
加载结束后,浏览器会触发domComplete事件,然后是onLoad事件。
domInteractive is important because its timing is recognized as a measure of perceived loading speed. See the MDN for more.
domInteractive很重要,因为它的时间被认为是感知加载速度的量度。更多信息请参阅MDN。
保持顺序
Another case pro defer: scripts marked async are executed in casual order, when they become available. Scripts marked defer are executed (after parsing completes) in the order which they are defined in the markup.
标记为async的脚本按加载完的顺序执行。标记为defer的脚本按定义的顺序执行(在解析完成之后)。
请直接告诉我最优解
The best thing to do to speed up your page loading when using scripts is to put them in the head, and add a defer attribute to your script tag:
使用脚本时,为页面加载提速的最佳方式是将脚本放在head标签中,并且添加defer属性。
1 | <script defer src="script.js"></script> |
This is the scenario that triggers the faster domInteractive event.
这是更快触发domInteractive时间的场景。
Considering the pros of defer, is seems a better choice over async in a variety of scenarios.
考虑到defer的优点,在各种场景看起来都比async更好。
Unless you are fine with delaying the first render of the page, making sure that when the page is parsed the JavaScript you want is already executed.
除非你想要延迟页面的首次渲染,否则确保在解析页面时你已经执行了所需的JavaScript。
最后放个原文链接bye👋~