在 HTML 页面中加载 JS 脚本文件时,需要注意不要损害页面的加载性能。将 JS 脚本添加到 HTML 页面的位置和方式会影响加载时间。
传统上,脚本通过以下方式包含在页面中:
<script src="script.js"></script>
当 HTML 解析器解析到这一行时,就会发出获取脚本的请求,并执行该脚本。
一旦这个过程完成,解析就可以恢复,并且可以分析 HTML 的其余部分。
可以想象,这个操作会对页面的加载时间产生巨大的影响。
如果脚本的加载时间比预期的要长一些,例如网络速度有点慢,或者您使用的是移动设备,连接有点不稳定,那么访问者可能会看到一个空白页,直到脚本加载完成并执行。
脚本在页面中的位置问题
当你第一次学习 HTML 时,你会被告知脚本标签位于 标签中:
<html>
<head>
<title>Title</title>
<script src="script.js"></script>
</head>
<body>
...
</body>
</html>
正如我前面所说,当解析器发现这一行时,它会去获取脚本并执行它。完成这项任务后,它会继续解析 body。
这样做不好,因为会产生大量延迟。解决这个问题的一个常用方法是将 script
标签放在页面底部,也就是结尾的 </body>
标签之前。
这样,script 脚本就可以在页面全部解析和加载完毕后再加载和执行,与将 script 脚本放在 head
标签中相比,这是一个巨大的提升。
如果你需要支持旧版浏览器,而这些浏览器又不支持 HTML 的两个相对较新的特性:async
和 defer
,那么这就是您能做的最好的事情。
async 和 defer
async 和 defer 都是布尔值树形,它们的用法类似:
<script async src="script.js"></script>
<script defer src="script.js"></script>
如果同时指定这两个参数,在现代浏览器中 async 优先级更高,而支持 defer 但不支持 async 的旧版浏览器将退回到 defer。
For the support table, check caniuse.com for async caniuse.com/#feat=scrip… and for defer caniuse.com/#feat=scrip…
这些属性仅在页面 head
标签中使用 script
时才有意义,如果像我们上面看到的那样将 script
放在正 body
底部,则这些属性毫无用处。
性能比较
head
标签中的 script
不使用 defer 或 async
下面是将 script
放到页面 head
中加载脚本的方式,既没有使用 defer
,也没有使用 async
。
解析过程会暂停,直到获取并执行脚本。执行完毕后,继续解析。
body
标签中的 script
不使用 defer 或 async
下面是一个页面在不使用 ·defer
或 async
的情况下加载脚本的过程,将其放在 body
标签的末尾,就在 </body>
之前:
解析过程没有任何停顿,解析完成后,脚本将被获取并执行。解析是在下载脚本之前完成的,因此用户看到的页面比上一个示例要早得多。
head
标签中的 script
使用 async
下面是一个页面使用 async
加载脚本,并将其放在 head 标签中:
脚本以异步的方式获取,获取脚本后,HTML 解析会暂停转而去执行 script 脚本,脚本执行完成后才会恢复 HTML 的解析。
head
标签中的 script
使用 defer
下面是一个页面使用 defer
加载脚本,并将其放在 head 标签中:
脚本是异步获取的,只有在 HTML 解析完成后才会执行。
解析 HTML 结束的时间与我们将脚本放在 body
标签末尾的时间相同,但总的来说,脚本执行结束的时间要早得多,因为脚本被下载和HTML 解析是并行的。
因此,就速度而言,这是最理想的解决方案 🏆
阻塞解析
async
会阻止页面的解析,而 defer
不会。
阻塞渲染
async
和 defer
都不能保证阻塞渲染。这取决于你和你的的脚本(例如,确保脚本在 onLoad 事件后运行)。
domInteractive
标记为 defer
的脚本会在 domInteractive
事件后立即执行,而 domInteractive
事件会在 HTML 被加载、解析并构建 DOM 后发生。
此时,CSS 和图像仍处于解析和加载中的过程中。
一旦这个过程完成,浏览器将将会触发 domComplete
事件,然后触发 onLoad
事件。
domInteractive
非常重要,因为它的时间被认为是衡量加载速度的标准。更多信息,请参阅 MDN。
保持顺序
另一种情况是 defer
:标记为 async
的脚本在可用时按随意顺序执行。标记为 defer 的脚本会按照标记中定义的顺序执行(解析完成后)。
最佳实践
使用脚本时,加快页面加载速度的最佳方法是将脚本放在 head
标签中,并在 script
标签中添加 defer
属性:
<script defer src="script.js"></script>
这就是更快触发 domInteractive
事件的场景。
考虑到 defer
的优点,在各种情况下 async
似乎比异步更好。
除非你不介意延迟页面的首次呈现,否则请确保在解析页面时,你想要的 JavaScript 已经执行。
原文链接:https://juejin.cn/post/7333937659838627892 作者:夏影_July