浏览器的渲染流程:
- HTML解析
- 计算样式
- 布局
- 分层
- 生成绘制指令
- 分块
- 光栅化
- 绘制
一、HTML解析(渲染主线程)
输入:html字符串
- 首先拿到的是一个html的字符串,需要通过令牌化(Tokenizing)将字符转换为HTML标签(如
<div>
、<p>
) - 渲染主线程根据解析标签的层级关系来构建DOM树(Document Object Model)
- 遇到
<script>
标签会暂停HTML解析,等待js下载并执行完成后再继续解析(除非标记为async
或defer
),因为js文件可能会对dom元素进行修改,要构建正确结构的DOM树,就要等待js执行完成。所以:js的下载和执行会阻塞渲染主线程 - 遇到
<link>
或<style>
会触发 CSS 解析,将CSS样式规则构建为CSSOM树(CSS Object Model),css的解析会阻塞渲染 - css文件的下载在预解析线程,不会阻塞渲染主线程
输出:DOM树和CSSOM树
二、计算样式(渲染主线程)
输入:DOM树和CSSOM树
- 拿到了DOM树和CSSOM树,要把样式给对应的dom节点安上,所以合并DOM和CSSOM,生成渲染树(Render Tree)
- 每个DOM节点的所有样式都计算完,在这个过程中,预设值为变成绝对值,如 red 变成 rgb(255,0,0) ,相对单位变成绝对单位,如 em 变成 px
- 会过滤掉display为none的DOM节点
输出:渲染树(Render Tree)
三、布局(渲染主线程)
输入:渲染树(Render Tree)
- 有了样式信息,但是还不能绘制出来,因为还不知道具体的位置信息和尺寸信息
- 遍历渲染树的每个节点,根据 CSS 盒模型、浮动、定位等规则,计算每个节点的精确几何信息,如具体位置的x,y坐标,宽度、高度等。
- 当修改了节点的几何属性,如大小、位置,就需要重新计算布局,这个过程也叫做回流或者重排(reflow)
- 获取节点的几何属性时,如
offsetWidth
/getBoundingClientRect
/clientWidth
会强制重排
输出:布局(layout)
四、分层(渲染主线程)
输入:布局(layout)
- 将页面进行分层,之后某个层变化时,就可以单独更新这一个图层,从而避免了全页面的更新,提高效率。
- 滚动条一般会单独分层
- 影响分层的属性 : will-change ,z-indexposition:fixed/absolute +z-index 等
输出:多个分层
五、生成绘制指令(渲染主线程)
输入:多个分层
- 为每一个分层单独绘制对应的指令集,用来描述当前图层该如何绘制
输出:多个绘制指令集
六、分块(合成线程)
输入:多个分层
- 大尺寸图层(如全屏滚动区域)会占用大量的GPU内存,消耗资源效率低
- 合成线程会调用多个线程把每个分层都分成更小的块(Tile),通常为256x246或者512x512像素
输出:多个块
七、光栅化(光栅化线程+GPU)
输入: 多个块
- 光栅化将每个块转成位图,光栅化线程处理分块的像素填充,启用GPU加速光栅化,GPU会优先处理靠近视口区域的块
输出:多个位图
八、绘制(合成线程+GPU)
输入:多个位图
- 合成线程会根据所有的位图,计算QUAD信息,包括位置和几何变换等,合成最终屏幕图像,随后交给GPU渲染页面
- transform等变形就发生在合成线程,所以不会阻塞渲染主线程,性能更好