渲染过程
构建DOM树 样式计算 布局 分层 绘制 分块 光栅化 合成
构建DOM树
字节→字符→令牌→节点→对象模型(DOM)- 转换:字节转换为字符
- 令牌化: 将字符串转化为符合w3c html5标准的各种令牌,令牌具有特使含义和一组规则
- 词法分析: 令牌转换成定义其属性和规则的“对象”
- 构建DOM树: 根据标记之间的不同关系构建树形结构的dom树
样式计算
格式化样式表- 将字节流转化为浏览器能够识别的stylesheets CSSOM树
标准化样式表- 将css样式标准化,比如将浏览器不能识别的em转化为px
计算每个DOM节点的具体样式继承:每个子节点会默认去继承父节点的样式,如果父节点 中找不到,就会采用浏览器默认的样式,也叫UserAgent样式层叠:定义如何合并来自多个源的属性值的算法
- 样式计算完之后 样式值会被挂载到window.getComputedStyle中可以通过js拿到
布局 layout Tree
创建布局树: 在DOM树上不可见的元素,head元素,meta元素等,以及使用display:none属性的元素,最后都不会出现在布局树上,所以浏览器布局系统需要额外去构建一棵只包含可见元素布局树- 遍历DOM树可见节点,并把这些节点加到布局树中
- 对于不可见的节点,head,meta标签和属性为display:none的标签等都会被忽略
布局计算
分层
生成图层树(Layer Tree)拥有层叠上下文属性的元素会被提升为单独一层并不是布局树的每个节点都包含一个图层,如果一个节点没有对应的层,那么这个节点就从属于父节点的图层。需要裁剪(clip)的地方也会创建图层图层绘制接下来我们就要开始绘制操作了,实际上在渲染进程中绘制操作是由专门的线程来完成的,这个线程叫合成线程。
分块
合成线程会讲图层划分为图块(tile)
光栅化
- 合成线程会按照视口附近的图块来优先生成位图,实际生成位图的操作是由栅格化来执行的。所谓栅格化,是指将图块转换为位图。
- 图块是栅格化执行的最小单位渲染进程中专门维护了一个栅格化线程池,专门负责把图块转换为位图数据
- 合成线程会选择视口附近的图块(tile),把它交给栅格化线程池生成位图
- 生成位图的过程实际上都会使用 GPU 进行加速,生成的位图最后发送给合成线程
合成操作
- 栅格化操作完成后,合成线程会生成一个绘制命令,即"DrawQuad",并发送给浏览器进程
- 浏览器进程中的viz组件接收到这个命令,根据这个命令,把页面内容绘制到内存,也就是生成了页面
回流/重排
- 对 DOM 结构的修改引发 DOM 几何尺寸变化
重绘
- 不改变元素的位置 只改变元素的样式
- 回流一定重绘,重绘不一定回流
- 由于跳过了布局和分层 比回流性能消耗小
合成
- 改了一个既不要布局也不要绘制的属性,那么渲染引擎会跳过布局和绘制,直接执行后续的合成操作,这个过程就叫合成 比如用transform实现动画,而合成会使用gpu加速所以效率更高
性能优化
- 使用createDocumentFragment进行批量的 DOM 操作
- 对于 resize、scroll 等进行防抖/节流处理。
- 动画使用transform或者opacity实现
- 将元素的will-change 设置为 opacity、transform、top、left、bottom、right - 。这样子渲染引擎会为其单独实现一个图层,当这些变换发生时,仅仅只是利用合成线程去处理这些变换,而不牵扯到主线程,大大提高渲染效率。
- 对于不支持will-change 属性的浏览器,使用一个3D transform属性来强制提升为合成 transform: translateZ(0);
- rAF优化等等