总结 scripts 阻塞 HTML 解析
看了一些类似文章,里面有这样一些表述:解析 HTML,DOM 解析等;我们统一下表述:下载完 HTML 文件后,浏览器会解析 HTML,目的是为了构建 DOM 结构 或 生成 DOM 树。
内联 scripts
<html>
<head></head>
<body>
<script>
console.log('irene')
</script>
</body>
</html>
解析 HTML 过程中遇到 内联 scripts
会暂停解析,先执行 scripts,然后继续解析 HTML。
普通外联 scripts
<script src="index.js"></script>
解析 HTML 过程中遇到 普通外联 scripts
会暂停解析,发送请求并执行 scripts,然后继续解析 HTML。如下图所示,绿色表示 HTML 解析;灰色表示 HTML 解析暂停;蓝色表示 scripts 下载;粉色表示 scripts 执行。
defer scripts
<script defer src="index.js"></script>
解析 HTML 过程中遇到 defer scripts
不会停止解析,scripts 也会并行下载;等整个 HTML 解析完成后按引用 scripts 的顺序执行。defer scripts 在 DOMContentLoaded 事件触发之前执行。defer 属性只能用于外联 scripts。
浏览器并行下载多个 defer scripts
,文件小的 scripts 很可能先下载完,defer 属性除了告诉浏览器不去阻塞 HTML 解析,同时还保证了defer scripts 的相对顺序。即使 small.js 先下载完,它还是得等到 long.js 执行完再去执行。
async scripts
<script async src="index.js"></script>
解析 HTML 过程中遇到 async scripts
不会停止解析,scripts 也会并行下载;scripts 下载完之后开始执行,阻塞 HTML 解析。async scripts
的执行顺序和它的引用顺序不一定相同。async scripts 可能在 DOMContentLoaded 事件触发之前或之后执行。如果 HTML 先解析完 async scripts
才下载完成,此时 DOMContentLoaded 事件已经触发, async scripts
很有可能来不及监听 DOMContentLoaded 事件。async 属性只能用于外联 scripts。
浏览器并行下载多个 async scripts
,文件小的 scripts 很可能先下载完,先下载完就先执行了,它无法保证按 async scripts
的引用顺序执行。
defer VS async
在实践中,defer 用于需要整个 DOM 或其相对执行顺序很重要的 scripts。而 async 则用于独立的 scripts,如计数器或广告,而它们的相对执行顺序并不重要。
dynamic scripts
let script = document.createElement('script');
script.src = "/article/script-async-defer/long.js";
document.body.append(script);
脚本一旦被 append 到文档中就开始下载,动态脚本在默认情况下表现的像 async scripts
,即先下载完先执行;可以显示设置 script.async = false,这样 scripts 的执行顺序就会和 defer scripts
表现的一致。
这两篇文章中,文一说 defer scripts
会阻塞 HTML 解析,文二说 defer scripts
不会阻塞 HTML 解析。其实两者的想法是一致的:即 defer scripts
的下载不会阻塞 HTML 解析,且执行是在构建完 DOM 之后;之所以有两种不同的表述是因为文一定义阻塞 HTML 解析的标准:是否在 DOMContentLoaded 之前执行,在之前执行就是阻塞 HTML 解析,否则就是不会;defer scripts
是在构建完 DOM 之后,DOMContentLoaded 之前执行的,所有文一认为 defer scripts
会阻塞 HTML 解析。文二说 defer scripts
不会阻塞 HTML 解析就很好理解了。
作者:小被子
链接:https://juejin.cn/post/7027673904927735822