logo
Published on

浏览器架构与渲染流程

Authors

概念

概念/术语定义
DOM (文档对象模型)HTML文档的内部树状结构,表示元素及其关系
CSSOM (CSS对象模型)CSS规则的结构化表示
计算样式 (Computed Style)每个节点的最终样式
布局树 (Layout Tree)基于DOM和样式计算出的视觉结构,用于确定位置和大小。
绘制记录 (Paint Records)描述如何绘制元素的指令。
合成 (Compositing)将多个图层组合成最终屏幕图像的过程。
图层 (Layers)可独立更新的页面部分,某些元素(如position: fixed)会创建独立图层。
瓦片 (Tiles)页面的小块区域,便于光栅化和缓存,提升渲染效率。
光栅化 (Rasterization)将矢量图形转为位图
重新布局 (Reflow)因DOM或CSS更改触发的布局重新计算,可能影响性能。
重绘 (Repaint)当元素外观更改时,重新绘制部分页面。
GPU加速 (GPU Acceleration)使用图形处理单元加速渲染任务,提升复杂图形和动画性能。
Draw QuadDraw Quad(绘制四边形)是合成线程(Compositor Thread)在将页面内容渲染到屏幕上时使用的一种核心概念。它本质上是一个绘制指令,表示将某个特定的图像区域(通常是光栅化后的位图或纹理)绘制到屏幕上的某个位置。Draw Quad 是合成阶段的一个关键中间产物,用于将多个图层(Layers)和瓦片(Tiles)组合成最终的屏幕图像

测试工具

chorme Performance

webpagetest:在资源URL左侧用橙色圆圈标记渲染阻塞资源

工作原理和过程

浏览器架构

画板

浏览器架构

  • Browser Process: 控制应用程序的“chrome”部分,包括地址栏、书签、后退和前进按钮。还处理网络浏览器中不可见的部分,例如网络请求和文件访问
  • Render Process: Controls anything inside of the tab where a website is displayed

多进程的好处

每个选项卡的都开一个渲染进程,当一个进程没有响应时,不会影响其他选项卡内容的渲染

节省更多内存-Chrome中的服务化

当 Chrome 在强大的硬件上运行时,它可能会将每个服务拆分为不同的进程以提供更高的稳定性,但如果它在资源受限的设备上,Chrome 会将服务整合到一个进程中以节省内存占用

Per-frame renderer processes -Site isolation

Site isolation为每个站点iframe运行单独的渲染器进程。同源策略是网络的核心安全模型,它确保一个站点在未经同意的情况下无法访问其他站点的数据

渲染流程

解析HTML(DOM tree)

  1. 主线程(Main Thread)开始解析并构建文档对象模型(DOM)->DOM Tree

  2. 预加载扫描器(Preload Scanner)

    1. 主线程在解析HTML的同时,运行一个轻量级的预加载扫描器。

    2. 扫描器识别并提前加载子资源,例如:

      1. 提前请求图片。

      2. 加载CSS文件。

      3. 加载JavaScript文件。

  3. 优化加载优先级

    1. 异步加载脚本,不阻塞HTML解析

    2. 延迟到HTML解析完成后执行。

    3. 提示浏览器提前加载关键资源

  4. DOM树是后续渲染的基础,预加载扫描器通过并行加载资源减少等待时间

样式计算(DOM tree+CSSOM->Composer tree)

CSSOM与DOM树共同决定了页面的视觉表现,计算样式是布局的基础

  1. CSS解析
    1. 主线程读取CSS文件(包括外部CSS、内联样式)。
    2. CSS解析器将CSS代码转换为样式表对象(CSSOM,CSS Object Model)

即使不添加样式,dom结构自己也有默认样式

  1. 计算样式(Computed Style)
  2. 主线程遍历DOM树,为每个节点匹配CSS规则,计算最终样式->composer tree

布局(composer tree=>layout tree)

有了DOM树和计算样式,浏览器进入布局阶段,生成布局树并计算元素的位置和大小,详细了解这部分内容可以阅读这里,布局树定义了页面元素的最终位置和大小,是绘制的前提

  1. 布局树(Layout Tree)构建
    1. 布局树基于DOM树和CSSOM,但有所筛选:
      1. 排除display: none的元素(完全不可见)。
      2. 包括伪元素
    2. 布局树节点对应于页面的视觉结构
  2. 盒模型计算
  3. 位置和大小计算

绘制(layout tree->Paint Records)

布局完成后,浏览器生成绘制指令,准备将页面内容渲染到屏幕上,绘制阶段将布局树的视觉信息转化为像素级的渲染指令

  1. 生成绘制记录(Paint Records)
  2. 绘制顺序由z-index和堆叠上下文决定
  3. 性能影响
    1. 绘制是CPU密集型任务,频繁更新(如动画)可能导致性能问题。
    2. 浏览器的目标是保持60帧每秒(16.6ms/帧),若主线程被JavaScript阻塞,可能导致卡顿

合成

绘制完成后,主线程将任务移交给合成线程(Compositor Thread),完成最终图像的生成和显示

  1. 提交绘制记录
    1. 主线程将绘制记录提交给合成线程,释放自身以处理其他任务
  2. 图层(Layers):合成线程将页面分成多个图层
  3. 瓦片(Tiles):每个图层被进一步分成小块(瓦片),通常为256x256或512x512像素。
    1. 瓦片化便于缓存和并行处理
  4. 光栅化(Rasterization:合成线程将瓦片转换为位图(Bitmap),并存储到GPU内存中
    1. GPU加速的光栅化显著提升性能
  5. 生成Draw Quad:合成线程为每个瓦片创建 Draw Quad,指定其纹理和屏幕坐标
    1. 考虑图层间的叠放顺序和透明度
  6. 显示(Viz组件)
    1. 合成线程将 Draw Quad 列表发送给浏览器进程的 Viz 组件,Viz 组件利用 GPU 将 Draw Quad 渲染为最终图像,显示在屏幕上。
    2. 为加快初次渲染,Viz可能先显示低分辨率图像,随后替换为高质量版本

实践

非快速滚动区域的优化

事件委托会将目标元素区域都标记为非快速滚动区域,这样合成器线程每次都要等待主线程工作后才会工作,就丧失了流畅的用户体验,可以通过在监听事件加passive:true解决,这向浏览器提示您仍想在主线程中收听事件,但合成器也可以继续合成新帧

document.body.addEventListener(
  'touchstart',
  (event) => {
    if (event.target === area) {
      event.preventDefault()
    }
  },
  { passive: true }
)

获取完整的事件触发

对于大多数 Web 应用程序,合并事件应该足以提供良好的用户体验。但是,如果您正在构建诸如绘图应用程序并根据 touchmove坐标放置路径之类的东西,则可能会丢失中间坐标以绘制平滑线。在这种情况下,可以使用getCoalescedEvents指针事件中的方法来获取有关这些合并事件的信息

window.addEventListener('pointermove', (event) => {
  const events = event.getCoalescedEvents()
  for (let event of events) {
    const x = event.pageX
    const y = event.pageY
    // draw a line using x and y coordinates.
  }
})

性能优化

GPU加速

.transform-gpu {
  --tw-translate-x: 0;
  --tw-translate-y: 0;
  --tw-rotate: 0;
  --tw-skew-x: 0;
  --tw-skew-y: 0;
  --tw-scale-x: 1;
  --tw-scale-y: 1;
  transform: translate3d(var(--tw-translate-x), var(--tw-translate-y), 0) rotate(var(--tw-rotate))
    skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x))
    scaleY(var(--tw-scale-y));
}

参考