了解浏览器渲染过程

了解浏览器渲染过程

参考链接:https://juejin.im/post/5c24d736f265da614b120d4a

https://blog.csdn.net/u013510838/article/details/55271887

视频教程

先说结论

  • 处理HTML标记数据并生成DOM树。
  • 处理CSS标记数据并生成CSSOM树。
  • DOM树与CSSOM树合并在一起生成渲染树。
  • 遍历渲染树开始布局,计算每个节点的位置信息。
  • 将每个节点绘制到屏幕。

详细的过程

1.构建DOM(Document Object Model)树

WX20190107-171231@2x

#####

1)浏览器从磁盘或者网络读取HTML的字节数据(网络中传输的内容就是0和1),根据指定编码格式(UTF- 8)转成字符串(代码)
2)字符串转成Token(这一步做的是取得尖括号中的标识,标识出谁是开始标签,谁是结束标签,谁是内容,谁是谁的子节点,这一步由令牌生成器完成)

WX20190107-171903@2x

3)在转换Token的过程中,同时也在消耗Token,会生出节点对象,构建DOM树(当所有Token消耗完,DOM树也构建完成)
2.构建CSSOM(Cascading Style Sheets Object Model)树
WX20190107-174533@2x
过程与构建DOM树类似,只是最后是构建CSSOM

还有就是css在下载完成并且构建完成CSSOM树之前,浏览器不会进行渲染。因为css支持重新定义和优化样式(其实就是后面重写之前的属性和新增属性),要得到最终的CSSOM树之后,才会渲染。

这里有个需要注意的就是,比如我在写sass的时候,它是支持嵌套的,一层层的写的很爽,然后写了层级非常的深,编译之后的css层级也会变的很深。但是浏览器递归 CSSOM 树,然后确定具体的元素到底是什么样式是很耗费性能的,所以还是不要写太深。多使用classid

举个🌰,哪个渲染的比较快,单选

WX20190109-130606@2x

答案: h1

h1,当找到这个节点的时候就设置font-size:16px,而第二个,需要先找到p节点,再向上遍历DOM树,父节点有div的时候,才应用font-size:12px。所以,更加具体的描述(比如div下面的p标签这种),需要浏览器做更多的工作。

3.构建渲染树

WX20190107-175749@2x

在构建DOM树,和CSSOM树之后,渲染树只会包括需要显示的节点和这些节点的样式信息,如果节点display:none,就不会在渲染树中显示。

一个小问题:display:none;visibility: hidden;的区别(简单的理解)

display:none;:从文档流中移除,浏览器不解析(元素需要显示的话会引起回流和重绘)。

visibility: hidden;:理解为透明(元素需要显示的话不会引起回流)。

4.布局(回流)

布局是一个递归过程,从根开始,计算每个节点的位置信息和宽高信息。

5.绘制

将渲染树转换成屏幕上的像素。

有个🌰,哪个节点绘制的比较快

WX20190109-145317@2x

答案:第二个绘制的成本比较低

6.练习题

WX20190109-150842@2x

答案:WX20190109-150730@2x

7.JavaScript与渲染过程(对渲染过程的影响)

记得宝哥第一次宝哥问我为什么脚本需要放在尾部,那时候我说:浏览器渲染时候,会被脚本阻塞,如果脚本执行的时间比较久,因为停止渲染就可能会出现白屏一会的情况。宝哥说算是说对了一部分吧。。

1)script标签在文档中的表现,举个🌰

WX20190109-221825@2x

这一段代码,在构建DOM树的时候,遇到了script标签,于是DOM树停止构建,等待JavaScript引擎执行完之后,并加上with JavaScript文本,再恢复DOM树构建。

2)第二个🌰,假如外部引入JavaScript

WX20190109-222515@2x

这一段代码,在构建DOM树的时候,遇到了script标签,于是DOM树停止构建,获取write.js文件(我们都知道获取文件是需要时间的),等待JavaScript引擎执行完之后,并加上with JavaScript文本,再恢复DOM树构建。

3)第三个🌰,p标签会显示什么颜色

WX20190109-223219@2x

先说结果:文字变成了红色。

这一段代码发生了什么:发送请求,获取响应之后开始构建DOM树,发现了CSS并发送请求,解析器继续执行,发现了script标签,这时候会阻止脚本的执行(不知道脚本执行了什么,可能操作DOM,可能访问或修改CSS),直到CSSOM树构建完毕,才执行JavaScript,最后继续构建DOM树。

WX20190109-225049@2x

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>

WX20190109-235625@2x

分别是: 异步 内联 阻止

总结

同步的情况:

​ 当没有引入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 支持)