加载阻塞
CSS阻塞
CSS 属于 render blocking resource,无论外链或内联,都会阻塞渲染树的渲染。一般情况下,CSS 会延迟脚本执行和 DOMContentLoaded 事件。
<body>
<h1>Tate</h1>
<script>
function print(){
console.log('first print', document.querySelectorAll('h1'));
}
print();
setTimeout(print);
</script>
<!-- 外链 -->
<link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css">
<h1>Snow</h1>
<!-- 内联 -->
<script> console.log('second print'); </script>
<style> h1{color: orange;} </style>
</body>
当注释掉 CSS 外链时,打印顺序为
first print NodeList [h1]
// 页面出现 h1 标签内容,即渲染完成
second print
first print NodeList(2) [h1, h1] // 异步
当加载 CSS 样式文件时,此时通过 Chrome Network 调试工具勾选 “Disabled cache”,再设置 “Throtting” 来模拟缓慢网络下的加载过程。打印顺序为
first print NodeList [h1]
first print NodeList(2) [h1, h1]
// ---------------加载 CSS 文件中,延迟---------------
// 页面出现 h1 标签内容,即渲染完成
second print
因此可看出
- CSS 加载延迟了后续脚本的加载;
- 加载样式文件时,渲染一直未完成,说明 CSS 阻塞渲染;
- 第一次打印为 h1,说明 JS 会阻塞后续的解析和渲染,以及其他资源的加载;
- 第二次打印为 h1 和 h2,说明 CSS 不阻塞 DOM 的解析;
利用媒体类型和查询来解除 CSS 对渲染的阻塞,详见上一节页面渲染。
JS阻塞
阻塞 DOM 解析
- JS 属于 parser blocking resource;
- 立即加载和执行;
- 除非将 JavaScript 显式声明为异步,否则它会阻止构建 DOM 和后续的渲染。
<body>
<h1>Tate</h1>
<script>
function print(){
console.log('first print', document.querySelectorAll('h1'));
}
print();
setTimeout(print);
</script>
<script src="https://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<h1>Snow</h1>
<script> console.log('second print'); </script>
<style> h1{color: orange;} </style>
</body>
打印顺序为
first print NodeList [h1]
first print NodeList [h1]
// 页面出现 'Tate'
// ---------------加载和执行 JS 文件中,延迟---------------
// 页面出现 'Snow'
second print
因此可看出
- JS 文件同步加载和执行。
- JS 会阻塞后续的解析和渲染,以及其他资源的加载;
样式表、图片等资源文件的下载不会暂停 DOM 解析。浏览器会并行地下载这些文件,但通常会限制并发下载数。
延迟脚本 defer
立即下载,但脚本会延迟到整个页面都解析完毕后再运行。
<!-- 只适用于外链 -->
<script defer src="script.js">
<!-- 内联无效 -->
<script defer> console.log('defer'); </script>
异步脚本 async
立即下载,期间不会阻塞 DOM 解析,等下载完成并执行时,才会阻塞解析。
<!-- 只适用于外链 -->
<script async src="script.js">
<!-- 内联无效 -->
<script async> console.log('async'); </script>
延迟和异步脚本一定会在 load 事件前执行,但可能在 DOMContentLoaded 事件触发之前或之后执行。
延迟脚本最终还是按照 js 顺序加载;异步脚本最终执行顺序不定。
动态插入
参考异步脚本载入提高页面性能一文的摘要:
- 动态插入的外部样式表或脚本不阻塞 DOM 解析或渲染。
- 动态插入的内联样式表或脚本会阻塞 DOM 解析和渲染。
<body>
<h1>Tate</h1>
<script>
function print(){
console.log('first print', document.querySelectorAll('h1'));
}
print();
setTimeout(print);
</script>
<!-- 动态插入外链样式,异步执行,不阻塞 DOM 解析和渲染 -->
<script>
var doc = document;
var bootcss = doc.createElement('link');
bootcss.rel = 'stylesheet';
bootcss.href = 'https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css';
var tate = doc.getElementById('tate');
doc.body.insertBefore(bootcss, tate);
</script>
<h1 id="tate">Snow</h1>
<script> console.log('second print'); </script>
</body>
参考链接
- Google - 使用 JavaScript 添加交互 By Ilya Grigorik
- Asynchronous vs Deferred JavaScript By Ire Aderinokun
- CSS/JS 对 DOM 渲染的影响 By harttle
- 异步渲染的下载和阻塞行为 By harttle