# 资源加载顺序
CSS和 JS 是否会阻塞页面解析渲染?
# CSS加载会阻塞DOM树解析和渲染吗
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
h1 {
color: red !important;
}
</style>
<link href="https://cdn.bootcss.com/bootstrap/4.0.0-alpha.6/css/bootstrap.css" rel="stylesheet">
<script>
function h() {
console.log(document.querySelectorAll('h1'))
}
setTimeout(h, 0)
</script>
</head>
<body>
<h1>这是红色的</h1>
</body>
</html>
我们假设 CSS 会阻塞DOM树解析和渲染,那么会出现以下情况:
在bootstrap.css还没加载完之前,下面的内容不会被解析渲染。那么我们一开始看到的应该是白屏,h1不会显示出来。并且此时console.log的结果应该是一个空数组。
# CSS会阻塞DOM树解析?
打开浏览器,我们发现在bootstrap.css还没加载完成的时候,h1并没有显示,但是此时控制台输出 [h1],可以得知:
DOM树至少已经解析完成到了h1那里,而此时css还没加载完成,也就说明,CSS并不会阻塞DOM树的解析。
# CSS加载会阻塞DOM树渲染?
上面操作可以发现,在 CSS 未加载完之前,页面都是处于白屏,CSS加载完后,红色字体才显示出来.下面的内容虽然解析了,但是并没有被渲染出来。所以: CSS加载会阻塞DOM树渲染。
# CSS加载会阻塞JS运行吗
<!DOCTYPE html>
<html lang="en">
<head>
<title>css阻塞</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script>
console.log('before css')
var startDate = new Date()
</script>
<link href="https://cdn.bootcss.com/bootstrap/4.0.0-alpha.6/css/bootstrap.css" rel="stylesheet">
</head>
<body>
<h1>这是红色的</h1>
<script>
var endDate = new Date()
console.log('after css')
console.log('经过了' + (endDate - startDate) + 'ms')
</script>
</body>
</html>
假设:CSS会阻塞后面的JS运行,在link后面的js代码,应该要在CSS加载完成后才会输出
经过运行我们发现,位于 CSS 加载前的 JS 先执行,位于CSS 后面的JS 等到 CSS 加载完后 才执行,并且经过了 169ms 才执行。我们得出结论: CSS 会阻塞后面JS的执行
# 原理解析
浏览器的渲染机制一般分为以下几个步骤:
- 处理 HTML 并构建 DOM 树 🌲
- 处理 CSS 构建 CSSOM 树 🌲
- 将 DOM 和 CSSOM 合并成一个渲染树 🌲
- 根据渲染树来布局,计算每个节点的位置
- 调用 GPU 绘制,合成图层,显示在屏幕
从图解可以看出
- DOM 解析和CSS解析是两个并行的进程,它们分别由不同的解析引擎去分析,这也解释了为什么CSS加载不会阻塞DOM的解析。
- 由于Render Tree是依赖于DOM Tree和CSSOM Tree的,所以他必须等待到CSSOM Tree构建完成,也就是CSS资源加载完成(或者CSS资源加载失败)后,才能开始渲染。因此,CSS加载是会阻塞Dom的渲染的
- 由于js可能会操作之前的Dom节点和css样式,因此浏览器会维持html中css和js的顺序。因此,样式表会在后面的js执行前先加载执行完毕。所以css会阻塞后面js的执行。
# DOMContentLoaded
对于浏览器来说,页面加载主要有两个事件,一个是 DOMContentLoaded,另一个是 onLoad
onLoad
等待页面的所有资源都加载完成才会触发,这些资源包括css、js、图片视频等
DOMContentLoaded
当页面的内容解析完成后,则触发该事件
正如我们上面讨论过的,CSS会阻塞DOM渲染和JS执行,而JS会阻塞DOM解析。那么我们可以做出这样的假设
- 当页面只存在css,或者js都在css前面,那么DomContentLoaded不需要等到css加载完毕。
- 当页面里同时存在css和js,并且js在css后面的时候,DomContentLoaded必须等到css和js都加载完毕才触发。
# CSS不会阻塞 DOM Ready
<!DOCTYPE html>
<html lang="en">
<head>
<title>css阻塞</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script>
document.addEventListener('DOMContentLoaded', function() {
console.log('DOMContentLoaded');
})
</script>
<link href="https://cdn.bootcss.com/bootstrap/4.0.0-alpha.6/css/bootstrap.css" rel="stylesheet">
</head>
<body>
</body>
</html>
可以看到,CSS还未加载完,就已经触发了 DOMContentLoaded事件了。因为CSS后面没有任何JS代码。
# CSS会阻塞 DOM Ready
<html lang="en">
<head>
<title>css阻塞</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script>
document.addEventListener('DOMContentLoaded', function() {
console.log('DOMContentLoaded');
})
</script>
<link href="https://cdn.bootcss.com/bootstrap/4.0.0-alpha.6/css/bootstrap.css" rel="stylesheet">
<script>
console.log('到我了没');
</script>
</head>
<body>
</body>
</html>
可以看到,只有在css加载完成后,才会触发 DOMContentLoaded 事件,因为 CSS 后面有JS代码
# 结论
- CSS加载 不会阻塞 DOM 🌲的解析
- CSS加载 会阻塞 DOM 🌲 的渲染
- CSS加载 会阻塞 后面JS 语句的执行
- CSS下面没有JS时 不会阻塞 DOM Ready
- CSS下面有JS时 会影响 DOM Ready