首先回顾下浏览器渲染页面的流程:
解析HTML构建DOM树
解析CSS构建Render树
解析css构建render树(将CSS代码解析成树形的数据结构,然后结合DOM合并成render树
布局render树(Layout/reflow),负责各元素尺寸、位置的计算
绘制render树(paint),绘制页面像素信息
浏览器会将各层的信息发送给GPU,GPU会将各层合成(composite),显示在屏幕上。
相关定论:
1.HTML和CSS的加载和解析是异步的,不会相互干扰
2.只有等dom树和cssom树构建完成并且合并成render树之后,页面才开始渲染,所以css的加载不会影响dom的解析,但会影响dom的渲染
JS阻塞DOM解析
当解析器遇到 script 标签时(不带有async/defer属性),浏览器会停止DOM的解析,会一直等到该 script 的加载并执行后,才继续往下解析。
比较合理的解释就是,首先浏览器无法知晓JS的具体内容,倘若先解析DOM,万一JS内部全部删除掉DOM,那么浏览器就白忙活了,所以就干脆暂停解析DOM,等到JS执行完成再继续解析。
CSS的加载会阻塞js的执行
Css 的加载会阻塞js的执行,这一点不是很好理解,看以下代码:
<head>
<link rel="stylesheet" href="./style.css?sleep=3000"> //假设css的加载会花3s
<script src="./index.js"></script>
</head>
<body>
<p>hello world</p>
</body>
我们从head开始解析,首先遇到link标签,去加载css(加载需要3s),此时浏览器不会停下来,会继续往下解析,然后遇到script标签,开始加载js,js很快就加载完了,但是js不会马上执行,因为它会等到上面的css资源加载完成之后再开始执行。从而导致了下面的p标签一直没有得到解析,因为上面说了js是会阻塞DOM的解析的,浏览器会等到js执行了才会继续往下解析DOM。
千万不要认为是:CSS的加载阻塞了DOM的解析。
流程是:
css和js可以同时去加载—>css加载很慢,即使js已经加载完了也会等到css加载完了才开始执行—>js一直不执行所以阻塞了DOM的解析
那么为什么js明明都加载完了,还要等css加载完才开始执行呢?
可以这样理解,浏览器并不知道js中的代码会干些什么,js可以去改动DOM,也可以获取/改变css样式。js要获取正确的样式就必须等css加载完
js会触发页面的渲染
大概意思是,浏览器在解析时,如果遇到了script标签,会先渲染一次这个script标签之前的DOM,然后再去加载和执行js。因为js是可以操作DOM的,如果浏览器不先去渲染一次,js获取的DOM就会是null。比如:
<body>
<p id="box1">world</p>
<script>
console.log(document.getElementById('box1'));
console.log(document.getElementById('box2'));
</script>
<p id="box2">hello</p>
</body>
上面代码的js是可以获取到id为box1的p标签,但是不能获取box2的p标签,原因很简单,因为script在box2前面。js执行的时候,box2还没有被解析和渲染。
另外说一下,浏览器解析DOM时,虽然会一行一行向下解析,但是它会预先加载具有引用标记的外部资源(例如带有src标记的script、link标签),而在解析到此标签时,则无需再去加载,直接运行,以此提高运行效率。