浏览器渲染原理

浏览器是如何渲染页面的?

当浏览器的网络线程收到 HTML 文档后,会产生一个渲染任务,并将其传递给渲染主线程的消息队列在事件循环机制的作用下,渲染主线程会从消息队列中取出渲染任务,然后执行渲染任务

渲染任务的执行过程如下: HTML 字符串 —> 解析 HTML —> 样式计算 —> 布局 —> 分层 —> 绘制 —> 分块 —> 栅格化 —> 合成与显示像素信息

  1. 解析 HTML (Parsing) a.开始解析前,会启动预解析县城率先下载外部 CSS 文件和 JS 文件。 b. 开始解析 HTML 后,遇到 HTML 标签,会生成对应的 DOM 节点。 - 当遇到 link 标签时,外部的 CSS 还没解析好而主线程不会等待,继续解析后续 HTML - 当遇到 script 标签时,必须暂停 HTML 解析,等待 JS 文件下载并执行完毕后,继续解析 HTML。因为 JS 可能会改变当前的 DOM 树。所以 JS 会阻塞 HTML 的解析 c. 最后都解析完成分别得到 DOM 树与 CSSOM 数

  2. 样式计算 (Style Calculation) 输入为 DOM 树与 CSSOM 数。样式计算的目的是为了计算出 DOM 树中每个节点的具体样式(例如把颜色变为 rgb 数值,em/rem 算 pixel 值等)

  3. 布局 (Layout) 输入为 整合了 CSS 的 DOM 树。布局的目的是为了计算出 DOM 树中每个节点的位置与大小。大部分时候,DOM 树与 Layout 树不会一一对应。例如display:none的节点没有几何信息就不会生成在布局树上。 最终输出为 Layout 树

  4. 分层 (Layer) 对 Layout 树进行分层,分层的好处在于将会对不同层的节点进行单独的处理,提高渲染效率。

  5. 绘制 (Paint) 为每一层生成绘制指令,用于描述这一层的内容该如何画出来。 以上操作都是渲染主线程完成的,接下来就是由合成线程来完成的

  6. 分块 (Tiling) 分块会将每一层分为多个小的区域。 完成绘制后,主线程将每个图层的绘制信息交给合成线程,合成线程会将每个图层分成多个小的区域,它会从线程池中拿取多个线程来完成分块工作。

  7. 光栅化 (Raster) GPU 进程处理光栅化将每个块变成位图,优先处理可视区域的块。

  8. 合成与显示像素信息 (Composite) 合成线程得到每个位图的屏幕位置,生成一个个 quad 信息。交给 GPU 进程,由 GPU 进程将像素绘制到屏幕上。

面试题

什么是回流 reflow?

reflow 的本质就是重新计算 Layout 树。 为了避免连续的多次操作导致布局树反复计算,浏览器会合并这些操作,当 JS 代码全部完成后再进行统一计算。所以,改动属性造成的 reflow 是异步完成的。 也同样因为如此,当 JS 获取布局属性时,就可能造成无法获取到最新的布局信息。 浏览器在反复权衡下,最终决定获取属性立即 reflow。

什么是重绘 repaint?

repaint 的本质就是重新根据分层信息计算了绘制指令。 当改动了可见样式后,就需要重新计算,会引发 repaint。 由于元素的布局信息也属于可见样式,所以 reflow 一定会引起 repaint。