You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
这里,我通过一个循环强行占用了 3 秒的 JS 线程,延迟去获取 dom 节点,假如 JS 执行不会阻塞 DOM 的解析,那么 log 出来的数组应该不为空
结果为
tag -> NodeList []
所以,事实就是 JS 的执行会阻塞 DOM 的解析,事实理应如此,假如 DOM 和 JS 执行是并行的,但是浏览器是不知道 script 里面未来将要执行的内容会不会改变我解析出来的 DOM,如果会(比如 document.write 这种操作),那浏览器就相当于白忙活了,所以还不如不解析先,等你 JS 搞完,我的 DOM 再进行解析,所以这里就有了一个优化点,为避免浏览器白屏时间过长,应该尽可能地把脚本放在后面再去执行,避免阻塞 DOM 的解析工作。
The DOMContentLoaded event fires when the initial HTML document has been completely loaded and parsed, without waiting for stylesheets, images, and subframes to finish loading.
所以,DOM 早已经解析好了,并没有等待外部样式加载完成,所以前面的脚本能够通过 setTimeout 来获取到 h1 节点,因此外部样式的加载并不会阻塞 DOM 树的解析,但是会阻塞 DOM 树的渲染(猜想应该是要等待 CSSOM 的完成,然后再合成渲染树,避免样式突然的变化而出现闪烁)
再上代码
<script>
const before = Date.now()
</script>
<script>
setTimeout(() => {
const h1Tags = document.querySelectorAll('h1');
console.log('before element -> ', h1Tags)
}, 0)
</script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.css">
<script>
const after = Date.now()
console.log('time offset -> ', after - before);
const tag = document.querySelectorAll('h1');
console.log('tag -> ', tag)
</script>
<body>
<h1>LOLOLO</h1>
</body>
这里我把 script 代码放在了外部样式 link 后面,同时加了个时间比对,输出结果为
before element -> NodeList []
time offset -> 2482
tag -> NodeList []
之前我有写过一篇 script 标签 async 和 defer 属性,简单介绍过其加载方式对页面加载的影响,这次再聊聊加载样式和 script 标签执行对页面加载的影响
Script
先来看看代码
这里,我通过一个循环强行占用了 3 秒的 JS 线程,延迟去获取 dom 节点,假如 JS 执行不会阻塞 DOM 的解析,那么 log 出来的数组应该不为空
结果为
所以,事实就是 JS 的执行会阻塞 DOM 的解析,事实理应如此,假如 DOM 和 JS 执行是并行的,但是浏览器是不知道 script 里面未来将要执行的内容会不会改变我解析出来的 DOM,如果会(比如 document.write 这种操作),那浏览器就相当于白忙活了,所以还不如不解析先,等你 JS 搞完,我的 DOM 再进行解析,所以这里就有了一个优化点,为避免浏览器白屏时间过长,应该尽可能地把脚本放在后面再去执行,避免阻塞 DOM 的解析工作。
CSS
说完内联脚本的执行问题,再来看下样式加载又有什么影响,先来看看代码(省略了部分代码,只展示相关部分)
这里我增加了一个外联的样式,并且在该样式加载写了个 setTimeout 去读取后面的 h1 标签,同时我将浏览器网络设置为比较慢的模式(network -> online 选项卡),此时输出
仔细观察可以发现,这行 log 会比页面上 "LOLOLO" 几个字更快打印出来,这是因为我们的外部样式请求会比较慢,我测试的时候时候是2s多,所以这几个字是2秒后才渲染在页面上的,而实际上 DOMContentLoaded 事件完成,才16ms,蓝色线,也远在请求完成之前,如下图
MDN 解析 DOMContentLoaded Event
所以,DOM 早已经解析好了,并没有等待外部样式加载完成,所以前面的脚本能够通过 setTimeout 来获取到 h1 节点,因此外部样式的加载并不会阻塞 DOM 树的解析,但是会阻塞 DOM 树的渲染(猜想应该是要等待 CSSOM 的完成,然后再合成渲染树,避免样式突然的变化而出现闪烁)
再上代码
这里我把 script 代码放在了外部样式 link 后面,同时加了个时间比对,输出结果为
前面的 setTimeout 也拿不到结果了,并且,time offset 是 2s 多,正是加载外部样式的时间!,由此
可以看出,加载样式是会阻塞后续的 script 执行,然后 script 又会导致后面的 dom 无法解析,都在等待该样式表的加载,再看看 DomContentLoaded 事件的时间,
可以看出,DOM 树解析的时间把加载样式的时间也算进去了,这也就是我们平常网上经常看到, script 不要紧跟在 link 后面,link 尽量在头部完成,而 script 尽量往后执行,如果必要,则把 script 放在 link 之前执行,这也是一条很重要的优化点。
The text was updated successfully, but these errors were encountered: