渲染流水线:渲染模块在执行过程中会被划分为很多子阶段, 输入的 HTML文件经过这些子阶段,最后在浏览器上输出像素。
子阶段:
开始每个子阶段都有其输入的内容
然后每个子阶段有其处理过程
最终每个子阶段会生成输出内容
流水线可分为如下几个子阶段:构建DOM树、样式计算、布局阶段、分层、绘制、分块、光栅化、合成
一.构建DOM树
浏览器无法直接理解和使用HTML,渲染引擎将HTML转换为浏览器能够理解的结构-DOM树
DOM和HTML的内容几乎一样,但DOM是保存在内存中的树状结构,可以通过JS来查询或修改内容
二.样式计算(Recalculate Style)
样式计算是为了计算出 DOM节点中每个元素的具体样式,在计算中需要遵守CSS的继承和层叠两个规则。
最终输出的内容是每个DOM节点的样式,并被保存在ComputedStyle的结构内
1. 把css转换成浏览器能够理解的结构
当渲染引擎接收到CSS文本时,会执行一个转换操作,将CSS文本转换为浏览器可以理解的结构——styleSheets。
styleSheets具备了查询和修改功能
2. 转换样式表中的属性值,使其标准化
比如 bold,red等等 这些会被转换成 700,rgb(255, 0, 0), 所以建议写代码的时候就使用标准值
3. 计算出DOM树中每个节点的具体样式
CSS继承规则: 每个DOM节点都继承了父节点的样式。
CSS层叠规则: 层叠是CSS的一个基本特征, 定义了如何合并来自多个源的属性值的法。
1. 若本身设置了样式,则继承的样式无效
2. !important具有最高权限
3. 行内式仅次于!important
4. (I-C-E)计算特指度, id特指度为100 class特指度为10 element标签特指度为1
5. 特指度相等的情况下,后面定义的样式层叠前面定义的样式
三.布局阶段
布局: 计算出DOM树中 可见元素 的几何位置
现在已经有了 DOM树 和 DOM树中元素的样式, 但是还不知道DOM元素的几何位置信息
1. 创建布局树
DOM树中还有许多不可见的元素,比如head标签,还有设置了display:none属性的元素
所以在显示之前,还会额外的构建一颗只包含 可见元素 的布局树
(1)遍历DOM树中的所有可见节点,并把这些节点添加到布局树中
(2)不可见节点会被布局树忽略掉
2. 布局计算
计算出布局树节点的坐标位置, 引用网络图片如下
四.分层
渲染引擎会为特定的节点生成专用的图层,并生成一颗对应的图层树(layer tree)
1.浏览器的页面实际上被划分成很多图层,这些图层叠加后合成了最终的页面
2.并不是布局树的每个节点都包含一个图层,如果一个节点没有对应的层,那么这个节点就从属于父节点的图层
(1).拥有 层叠上下文属性 的元素会被提升为单独的一层。
层叠上下文属性: 此处列出常用的css属性
文档根元素html
position: fixed,sticky的元素
position: absolute,relative 且z-index的值不为auto的元素
opacity属性值小于 1 的元素
filter,transform 属性值不为none的元素
(2).需要剪裁(clip)的地方也会被创建为图层
比如宽度,高度溢出,如果设置了overflow:hidden; 此时溢出的区域就被裁剪掉了
如果设置成了overflow:auto; 出现了滚动条,滚动条也会被提升为单独的一层
如下图:有三个图层,分别是 文档根元素html,以及x,y轴滚动条
图层树的构建之后,渲染引擎会对图层树中的每个图层进行绘制
五.图层绘制
渲染引擎会把一个图层的绘制拆分成很多小的绘制指令并输出
绘制列表只是用来记录绘制顺序和绘制指令的列表,实际上的绘制操作是由渲染引擎中的合成线程来完成的
六.栅格化(raster)操作
栅格化: 是指将图块转换为位图。
当图层绘制列表准好之后, 渲染进程的主线程 会把绘制列表提交给 渲染进程合成线程
渲染进程的合成线程:
合成线程会将图层划分为图块(tile)
合成线程会按照视口附近的图块来优先生成位图,实际生成位图的操作是由栅格化来执行的
渲染进程栅格化线程:
GPU栅格化: 使用GPU生成位图,并保存在GPU内存中
七.合成与显示
当所有都被光栅化,渲染进程的合成线程会生成一个绘制图块的命令'DrawQuad',然后将该命令提交给 浏览器主进程
浏览器主进程的viz组件接收到渲染进程的合成线程发过来来的'DrawQuad'指令后,将其页面内容绘制到内存中,最后再将内存能显示在页面上
总结:
在HTML页面内容被提交给渲染引擎之后,接上篇从url输入到页面展示
一.渲染引擎首先将 HTML 解析为浏览器可以理解的 DOM树
二.渲染引擎将CSS样式表转化为浏览器可以理解的styleSheets,计算出DOM树所有节点的样式
三.接着又计算每个元素的几何坐标位置,并将这些信息保存在布局树中
四.对布局树进行分层,并生成分层树
五.为每个图层生成绘制列表,并将其提交到合成线程
六.(1)合成线程将图层分成图块,并在光栅化线程池中将图块转换成位图
(2)合成线程发送绘制图块命令 DrawQuad 给浏览器进程
七.浏览器进程根据DrawQuad消息生成页面,并显示到显示器上
相关概念: 重排,重绘,合成
重排
比如通过js和css修改元素的几何位置属性,比如宽度,高度
重排需要更新完整的渲染流水线,所以开销也是最大的
重绘
比如通过js和css修改元素的背景颜色,并不改变元素的几何位置属性
重绘省去了布局和分层阶段,所以执行效率会比重排操作要高一些。
合成
使用CSS的transform属性,可以避开重排和重绘阶段,直接进入合成线程的子阶段
相对于重绘和重排,合成能大大提升绘制效率
js文件和css文件是否会阻塞dom树的合成?
浏览器主进程接收到“确认提交”的消息后,渲染进程便开始页面解析和子资源加载了
第一种情况, js为内联脚本
DOM解析器解析到 <script>console.log('hello word')</script>时
DOM解析器会先执行JS脚本,执行完成之后,再继续往下解析
第二种情况, js为外联脚本
DOM解析器解析到<script type="text/javascript" src="index.js"></script>时
先暂停DOM解析并下载index.js文件, 下载完成后先执行JS脚本,执行完成之后,再继续往下解析
第三种情况, js脚本改变了css样式,且css为外联方式
DOM解析器解析到 <script>document.body.style.background = 'skyblue'</script>时
这段代码访问了body元素的background样式且 <style type="text/css" src="index.css" />为外联方式那么这个时候需要等待这个样式被下载完成才能继续往下执行
结论: JS和CSS都有可能会阻塞DOM解析