内容简介:在探讨 CSS、JS 对阻塞行为前,先建立如下的 html,后续的探讨都在这个 html 的基础上进行。html 文件如下:可以预见的是 html 加载完毕后页面会呈现一个蓝色的正方形。
在探讨 CSS、JS 对阻塞行为前,先建立如下的 html,后续的探讨都在这个 html 的基础上进行。
html 文件如下:
<!DOCTYPE html>
<html lang="en">
<head>
<style>
div {
width: 100px;
height: 100px;
background: blue;
}
</style>
</head>
<body>
<div />
</body>
</html>
复制代码
可以预见的是 html 加载完毕后页面会呈现一个蓝色的正方形。
JS 的阻塞行为
-
<script src="script.js"></script>对于没有 async 和 defer 属性的 script,当浏览器解析到 script 标签时会立即加载并执行脚本,这会阻止 dom 的解析,也就说在 script 加载执行完成前 script 标签后的 dom 都不会解析。
加载脚本阻止 dom 解析
如下,head 内添加了一个内联脚本,一个外部脚本(sleep.js,为一个空文件),外部脚本将在服务端延迟 5 秒后返回。当 document.readyState 变为 interactive 可交互时,表明文档已解析完成,接近于 DOMContentLoaded 事件的触发。
<head> <script> console.log('start'); document.onreadystatechange = function () { if (document.readyState === "interactive") { console.log('DOMContentLoaded', document.body.children); } } </script> <script src="/sleep.js"></script> </head> 复制代码效果如下:
可以看到,页面刷新,start 首先执行,5 秒后 DOMContentLoaded 才执行,也就是说 js 的加载会阻止 dom 的解析。事实上多数浏览器在 js 加载执行时都会停止解析文档,因为 js 可能操作 dom。
执行脚本阻止 dom 解析
如下,head 内添加了两个内联脚本,第二个内联脚本将执行至少 5 秒钟。
<head> <script> console.log('start'); document.onreadystatechange = function () { if (document.readyState === "interactive") { console.log('DOMContentLoaded', document.body.children); } } </script> <script> var now = Date.now(); var isRun = true; while(isRun) { var time = Date.now(); if (time - now > 5000) { isRun = false; } } console.log('body', document.body); console.log('end'); </script> </head> 复制代码效果如下:
可以看到,页面刷新,start 首先执行,5 秒后 DOMContentLoaded 才执行,脚本执行完毕前 body 为 null,也就是说 js 的执行会阻止 dom 的解析。
-
<script async src="script.js"></script>async 属性会使脚本后续文档的加载渲染和脚本的加载执行并行进行。async 脚本在下载完成后立即执行,所以不能保证脚本的执行顺序,以乱序执行为主。此外,async 不支持内联脚本。
加载 async 脚本不阻止 dom 解析
如下,将"加载脚本阻止 dom 解析"例子中的脚本改成 async。
<script async src="/sleep.js"></script> 复制代码
页面刷新,可以看到 DOMContentLoaded 立即打印了,也就说带有 async 属性的脚本加载时不会阻塞 dom 的解析。
执行 async 脚本不阻止 dom 解析
添加如下代码到 sleep.js
var now = Date.now(); var isRun = true; while(isRun) { var time = Date.now(); if (time - now > 5000) { isRun = false; } } console.log('body', document.body); console.log('end'); 复制代码如下: 以 async 的方式加载 sleep.js,服务端立即返回 sleep.js。
<head> <script> console.log('start'); document.onreadystatechange = function () { if (document.readyState === "interactive") { console.log('DOMContentLoaded', document.body.children); } } </script> <script async src="/sleep.js"></script> </head> 复制代码页面刷新,可以看到 DOMContentLoaded 立即打印了,也就说带有 async 属性的脚本执行时不会阻塞 dom 的解析。
-
<script defer src="script.js"></script>defer 会使脚本后续文档的加载渲染和脚本的加载并行进行,但 defer 脚本的执行要在所有元素解析完成之后 DOMContentLoaded 事件触发前完成,它是按着脚本加载顺序进行执行。
加载 defer 脚本不阻止 dom 解析
将 "加载 async 脚本不阻止 dom 解析例子" 中 async 换成 defer
<script defer src="/sleep.js"></script> 复制代码
页面刷新,可以看到 DOMContentLoaded 立即打印了,也就说带有 defer 属性的脚本加载时不会阻塞 dom 的解析。
执行 defer 脚本不阻止 dom 解析
将 "执行 async 脚本不阻止 dom 解析例子" 中 async 换成 defer。
<script> console.log('start'); document.onreadystatechange = function () { if (document.readyState === "interactive") { console.log('DOMContentLoaded', document.body.children); } } </script> <script defer src="/sleep.js"></script> 复制代码页面刷新,可以看到 DOMContentLoaded 立即打印了,也就说带有 defer 属性的脚本执行时不会阻塞 dom 的解析。
CSS 的阻塞行为
在 html 的 head 标签内加上 script 标签和 css 的 link,main.css 在服务器端延迟 5 秒后返回。
<head>
<script>
document.onreadystatechange = function () {
if (document.readyState === "interactive") {
console.log('DOMContentLoaded', document.body.children);
}
}
</script>
<link rel="stylesheet" href="/main.css" />
</head>
复制代码
main.css 文件如下:
div {
background: red;
}
复制代码
效果如下:
可以看到,页面刷新时,立即打印出了 DOMContentLoaded,尽管 main.css 是在延迟 5 秒后返回的,也就是说在 css 加载完成之前 dom 就已经解析完成了,css 的加载并不会阻止 dom 的解析。此外,我们并没有看到蓝色的正方形,而一直是一个红色的正方形,这意味着浏览器在 css 加载解析完成前没有渲染它后面的 dom(如果不是,则先看到蓝色的正方形,再看到红色的正方形),而是在 css 加载解析后再进行渲染,也就是说 css 会阻塞页面的渲染。这种策略是能够说得通的,试想如果先呈现出一个样子,一会又变一下,体验会比较差,而且多次渲染也浪费性能。
另一方面,在最初的测试时 script 是 link 后边的,如下:
<head>
<link rel="stylesheet" href="/main.css" />
<script>
document.onreadystatechange = function () {
if (document.readyState === "interactive") {
console.log('DOMContentLoaded', document.body.children);
}
}
</script>
</head>
复制代码
结果是,等到 main.css 加载完成后才打印了 DOMContentLoaded,这似乎和 css 不阻止 dom 解析相悖。事实上,由于 script 可能去获取 style 信息,如果 css 没有加载完成,显然不能够获取正确的信息,因此部分浏览器会直接阻止后续 script 的执行。
结论
- 没有 async 和 defer 属性的 script 加载或执行都会阻塞 dom 的解析。
- 带有 async 或 defer 属性的 script 加载或执行都不会阻塞 dom 的解析。
- async 的脚本加载完毕后立即执行,不保证执行顺序,而 defer 脚本在 dom 解析完毕后才执行,基本能保证按着脚本加载顺序执行。
- css 的加载解析会阻塞后续的 script 执行,但不会阻塞 dom 解析。
- css 的加载解析会阻塞 dom 的渲染。
需要说明的是以上所有结论在不同浏览器不同的版本,所采取的策略并不完全一致,比如脚本加载执行时,chrome(v:74)会继续下载 link 指定的文件,而 safari(v:12.0.2)link 文件 的下载会被阻塞。
以上所述就是小编给大家介绍的《CSS与JS对DOM的阻塞行为》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- Node.js 指南(阻塞与非阻塞概述)
- Node.js 回调函数 阻塞与非阻塞
- 明明白白学 同步、异步、阻塞与非阻塞
- 从 Linux 源码看 socket 的阻塞和非阻塞
- 分布式系统关注点——阻塞与非阻塞有什么区别?
- Netty基础系列(2) --彻底理解阻塞非阻塞与同步异步的区别
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
How to Build a Billion Dollar App
George Berkowski / Little, Brown Book Group / 2015-4-1 / USD 24.95
Apps have changed the way we communicate, shop, play, interact and travel and their phenomenal popularity has presented possibly the biggest business opportunity in history. In How to Build a Billi......一起来看看 《How to Build a Billion Dollar App》 这本书的介绍吧!
CSS 压缩/解压工具
在线压缩/解压 CSS 代码
RGB HSV 转换
RGB HSV 互转工具