# 宏观流程
要了解浏览器渲染引擎工作原理,就要先了解一下浏览器架构涉及到哪些进程。
- 网络进程:负责网络资源的加载、下载、缓存等
- 渲染进程:负责页面的渲染、布局、绘制等
- 浏览器主线程:负责处理用户交互、事件、定时器等
- GPU 进程:负责页面的合成、绘制等
- 插件、扩展程序进程:负责插件、扩展程序的运行
在这些进程中,渲染进程是最重要的一个进程,我们在这里着重介绍。
# 渲染流水线
浏览器渲染引擎是浏览器的核心组件之一,它负责将 HTML、CSS、JavaScript 等代码转换为用户可以看到的页面。浏览器渲染引擎的主要工作流程如下:
# 1. 解析 HTML
其实是一个 Parse 的过程,将 HTML 字符串解析成 DOM 树,document 就是 DOM 树的根节点。
那么如果解析过程中遇到了 CSS、JS 资源怎么办呢?
对于 CSS,浏览器会额外开启一个预解析线程,然后它会创建一个 CSS 资源请求,将其发送到网络进程中,网络进程会将 CSS 资源下载下来,再进行解析,最后发送 CSSOM 树给渲染进程。这个过程不会阻塞 HTML 的解析。
而对于 JS,浏览器会先暂停 HTML 的解析,等 JS 下载解析、执行完之后,再继续解析 HTML。这是因为 JS 可能会修改 DOM 树。
可以引申出一个问题,既然执行 JS 会阻塞 HTML 的解析,那下载解析能不能像 CSS 一样做到并行呢?
可以通过 defer 和 async 来实现。并且 ES mudule 脚本也是类 defer 的方式。
# 2. 构建渲染树
渲染树是由 DOM 树和 CSSOM 树合并而成的。
渲染树的构建过程是一个递归的过程,从根节点开始,依次遍历每个节点,将其对应的 CSSOM 节点合并到 DOM 节点中。
于是可以得到一棵带有样式的 DOM 树,这棵树就是渲染树。
# 3. 布局树
刚刚的渲染树是带有样式的 DOM 树,但是它还没有确定每个元素的位置和大小。
所以浏览器会为每个元素计算出一个布局信息,这个信息就是布局树。
布局树的构建过程也是一个递归的过程,从根节点开始,依次遍历每个节点,计算出每个节点的位置和大小。
并且布局树的结构可能与渲染树不同,例如 display: none
的节点没有几何信息,也就不会被放到布局树中,或者是为了符合渲染规则放置的匿名行盒、匿名块盒。
布局树的每一个节点不是 DOM 对象,而是 C++ 对象。
# 4. 分层
主线程会使用一套复杂的策略对整个布局树中进行分层。
分层的好处在于,将来某一个层改变后,仅会对该层进行后续处理,从而提升效率。
通过 will-change 属性可以一定程序地影响分层结果。
# 5. 绘制
主线程会为每个层单独产生绘制指令集,用于描述这一层的内容该如何画出来。
绘制指令集是一个数组,每个元素都是一个绘制指令。
# 6. 分块
完成绘制后,主线程将每个图层的绘制信息提交给合成线程,剩余工作将由合成线程完成。
合成线程首先对每个图层进行分块,将其划分为更多的小区域, 通常会使用多线程进行优化。
# 7. 光栅化
合成线程会将每个块的内容转换为位图,用于在屏幕上显示。
位图是一个二维数组,每个元素都是一个像素。
GPU 进程会开启多个线程来完成光栅化,并且优先处理靠近视口区域的块。
# 8. 显示
合成线程会将每个块的位图发送给 GPU 进程,GPU 进程会将其合成到屏幕上。
# 延伸问题
# 1. 什么是重排?什么是重绘?
重排是指当一个元素的位置、大小、形状、内容发生改变时,影响到了布局树,会重新计算布局树,这就是重排。
浏览器一般会将多次的重排合并成一次重排,可以看作异步任务。
但是如果此时需要获取元素的位置、大小、形状、内容,因为需要获取最新状态,就会立即合并上面的重排而获取最新状态,这就是强制同步。
重绘是指当一个元素的颜色、背景、边框等属性发生改变时,不会影响到布局树,会重新绘制该元素,这就是重绘。
注意,重排和重绘是两个不同的概念,重排会导致重绘,但是重绘不一定导致重排。
# 2. 为什么要分层?
分层的好处在于,将来某一个层改变后,仅会对该层进行后续处理,从而提升效率。
如果没有分层,那么整个页面都会重新绘制,这是一个非常耗时的操作。
# 3. 为什么要分块?
分块的好处在于,将来某一个块改变后,仅会对该块进行后续处理,从而提升效率。
如果没有分块,那么整个页面都会重新绘制,这是一个非常耗时的操作。
# 4. 为什么 transform 比 left、top 更高效?
transform 的变换(如位移、旋转)发生在 合成(Composite)阶段,浏览器会跳过布局和重绘,直接由 GPU 在独立的图层(Layer)上处理变换,效率极高。
参考文章:
稀土掘金 - 图解「浏览器渲染原理」
稀土掘金 - 画了 20 张图,详解浏览器渲染引擎工作原理