了解浏览器渲染过程
参考链接:https://juejin.im/post/5c24d736f265da614b120d4a
https://blog.csdn.net/u013510838/article/details/55271887
视频教程
先说结论
- 处理
HTML
标记数据并生成DOM
树。 - 处理
CSS
标记数据并生成CSSOM
树。 - 将
DOM
树与CSSOM
树合并在一起生成渲染树。 - 遍历渲染树开始布局,计算每个节点的位置信息。
- 将每个节点绘制到屏幕。
详细的过程
1.构建DOM(Document Object Model)树
#####
1)浏览器从磁盘或者网络读取HTML的字节数据(网络中传输的内容就是0和1)
,根据指定编码格式(UTF- 8)转成字符串(代码)
。
2)字符串
转成Token
(这一步做的是取得尖括号中的标识,标识出谁是开始标签,谁是结束标签,谁是内容,谁是谁的子节点,这一步由令牌生成器完成)
3)在转换Token
的过程中,同时也在消耗Token
,会生出节点
对象,构建DOM
树(当所有Token消耗完,DOM树也构建完成)
2.构建CSSOM(Cascading Style Sheets Object Model)树
过程与构建DOM树类似,只是最后是构建CSSOM
树
还有就是css在下载完成并且构建完成CSSOM树之前,浏览器不会进行渲染。因为css支持重新定义和优化样式(其实就是后面重写之前的属性和新增属性),要得到最终的CSSOM树之后,才会渲染。
这里有个需要注意的就是,比如我在写sass的时候,它是支持嵌套的,一层层的写的很爽,然后写了层级非常的深,编译之后的css层级也会变的很深。但是浏览器递归 CSSOM 树,然后确定具体的元素到底是什么样式是很耗费性能的,所以还是不要写太深。多使用class
和id
。
举个🌰,哪个渲染的比较快,单选
答案: h1
h1
,当找到这个节点的时候就设置font-size:16px
,而第二个,需要先找到p
节点,再向上遍历DOM树,父节点有div的时候,才应用font-size:12px
。所以,更加具体的描述(比如div下面的p标签这种),需要浏览器做更多的工作。
3.构建渲染树
在构建DOM
树,和CSSOM
树之后,渲染树只会包括需要显示的节点和这些节点的样式信息,如果节点display:none
,就不会在渲染树
中显示。
一个小问题:display:none;
和visibility: hidden;
的区别(简单的理解)
display:none;
:从文档流中移除,浏览器不解析(元素需要显示的话会引起回流和重绘)。
visibility: hidden;
:理解为透明(元素需要显示的话不会引起回流)。
4.布局(回流)
布局是一个递归过程,从根开始,计算每个节点的位置信息和宽高信息。
5.绘制
将渲染树转换成屏幕上的像素。
有个🌰,哪个节点绘制的比较快
答案:第二个绘制的成本比较低
6.练习题
答案:
7.JavaScript与渲染过程(对渲染过程的影响)
记得宝哥第一次宝哥问我为什么脚本需要放在尾部,那时候我说:浏览器渲染时候,会被脚本阻塞,如果脚本执行的时间比较久,因为停止渲染就可能会出现白屏一会的情况。宝哥说算是说对了一部分吧。。
1)script
标签在文档中的表现,举个🌰
这一段代码,在构建DOM树的时候,遇到了script
标签,于是DOM树停止构建,等待JavaScript引擎执行完之后,并加上with JavaScript
文本,再恢复DOM树构建。
2)第二个🌰,假如外部
引入JavaScript
这一段代码,在构建DOM树的时候,遇到了script
标签,于是DOM树停止构建,获取write.js
文件(我们都知道获取文件是需要时间的),等待JavaScript引擎执行完之后,并加上with JavaScript
文本,再恢复DOM树构建。
3)第三个🌰,p标签会显示什么颜色
先说结果:文字变成了红色。
这一段代码发生了什么:发送请求,获取响应之后开始构建DOM树,发现了CSS并发送请求,解析器继续执行,发现了script标签,这时候会阻止脚本的执行(不知道脚本执行了什么,可能操作DOM,可能访问或修改CSS),直到CSSOM树构建完毕,才执行JavaScript,最后继续构建DOM树。
8.那么重要的就是优化方案了
那么是优化什么呢,当然是提高网页呈现的速度
1)资源缩小(删除注释,消除不必要的数据)
2)压缩
3)HTTP缓存
4)等待页面加载完毕,浏览器会发出onload事件,之后再执行脚本
onload与DOMContentLoaded的区别
onload:等待页面所有DOM,CSS,图片,脚本等的资源加载完毕
DOMContentLoaded:仅仅等待DOM加载完毕
5)引入外部的JavaScript,可以在script
标签内加上async
(内联的不能加),当浏览器发现有async
就不会阻止构建DOM树,同时JavaScript也不会因为需要等待CSSOM构建完毕而停止执行,脚本就会立即执行。
6)script
标签添加defer属性,会在页面加载完毕之后执行。
7)使用内联的CSS,同时异步加载阻塞渲染的样式
8)减少HTTP请求
9) 媒体查询(有一些 CSS 样式只在特定条件下才使用,那么没必要让他们阻塞渲染)。
其实看到这里我有一个问题没有懂,就是有人说css有可能阻止JavaScript执行,那肯定有不阻止的时候(这里忽略上面这种引入外部JavaScript添加async
的情况)。
将JavaScript放在css上方。(我认为是因为解析到script就阻止DOM构建,导致还没解析到引入css的标签,先执行JavaScript,之后才继续构建的原因,还要再查下资料)。
关于对内联,异步和阻止的分辨
- 阻止(Blocking):
<script src="anExteralScript.js"></script>
- 内联(Inline):
<script>document.write("this is an inline script")</script>
- 异步(Async):
<script async src="anExternalScript.js"></script>
分别是: 异步 内联 阻止
总结
同步的情况:
当没有引入JavaScript的时候:CSSOM树构建与DOM树构建互不影响。
当DOM树构建的时候碰到script标签,会让出优先权交给JavaScript引擎,执行完之后,再继续构建DOM。
当引入CSS在引入JavaScript上方的时候:CSSOM树构建会阻止JavaScript执行,JavaScript会阻止DOM树构 建,当CSSOM树构建完毕,执行JavaScript,执行完之后再继续构建DOM树。
当引入JavaScript在引入CSS上方的时候:则JavaScript执行不会被CSS阻止。JavaScript执行仍然会阻止DOM树构建。
异步的情况(引入了外部的脚本):
script标签添加了async属性:不会影响DOM树构建,JavaScript也不会等待CSSOM树构建完毕才执行。
script标签添加defer属性:等待页面加载完毕再执行,不会影响到其他的(不过似乎只有 Internet Explorer 支持)