【周五了,有什么事下周一再说吧】来看 浏览器渲染原理(从线程角度思考)

266 阅读6分钟

先来看一组 Chrome浏览器的架构图:

image.png

浏览器渲染主线程与合成线程都做了什么?

在现代浏览器中,渲染过程不仅仅依赖于单一的主线程,合成线程也发挥着重要作用。理解主线程和合成线程的分工与协作有助于我们更好地优化前端性能和用户体验。

一、浏览器渲染主线程

渲染主线程是浏览器处理大部分渲染任务的核心线程。它负责以下关键任务:

image.png

1.1 解析和构建DOM树

当浏览器接收到HTML文档时,主线程开始解析HTML并构建DOM树。它将HTML标签、属性和内容解析成DOM节点,形成一个树状结构。

1.2 解析和构建CSSOM树

主线程同时解析CSS文件,并将其转换为CSSOM树。CSSOM树表示了所有样式规则及其层级关系。

1.3 执行JavaScript

JavaScript的执行也是在主线程上进行的。JavaScript可以直接操作DOM和CSSOM,这意味着它可以修改页面结构和样式。由于JavaScript的执行可能会阻塞HTML解析,现代浏览器采用了异步执行策略(如asyncdefer)来优化性能。

1.4 样式计算

样式计算是指将CSSOM树中的样式规则应用到DOM树中的每个元素,计算出每个元素的最终样式。这个过程也称为“样式应用”(Style Application)或“样式解决”(Style Resolution)。

样式计算的步骤如下:

  1. 继承和层叠:确定哪些样式是继承的,以及哪些样式规则在层叠样式表(CSS Cascading)中优先级更高。
  2. 计算具体值:将相对值(如em%等)转换为绝对值(如px)。

1.5 分层

在样式计算完成后,浏览器会根据特定规则将页面内容分割成多个图层。这一步骤在主线程上进行,称为“分层”(Layering)。

分层的目的是为了优化页面渲染性能,特别是处理复杂的页面布局和动画时。常见的触发分层的条件包括:

  • 元素使用position: fixedposition: sticky
  • 元素使用CSS属性transformopacityfilter等。
  • 元素包含动画或3D变换。

1.6 生成渲染树

主线程将DOM树和CSSOM树结合生成渲染树。渲染树包含了所有可见的DOM节点及其样式信息,用于后续的布局和绘制。

1.7 布局和绘制

主线程负责布局和绘制操作:

  • 布局:确定每个渲染对象的位置和大小。
  • 绘制:将每个渲染对象转换为屏幕上的像素。

二、合成线程

合成线程在现代浏览器中引入,主要为了提高渲染性能,特别是提升页面滚动和动画的流畅度。它与主线程协同工作,但专注于特定的渲染任务。

2.1 图层的生成和管理

合成线程负责生成和管理图层(Layers)。当页面包含复杂的样式或动画时,浏览器会将不同部分的内容分配到不同的图层。

2.2 分块(Tiling)

为了更高效地管理和渲染图层,合成线程会将图层分割成多个小块(Tiles)。每个小块的大小通常为256x256或512x512像素。这些小块便于并行处理和增量更新。

2.3 光栅化(Rasterization)

分块后,每个小块需要进行光栅化,将矢量图形转换为位图。光栅化通常在GPU上完成,以提高性能。这一步骤将矢量图形信息(如路径、颜色、渐变等)转换成具体的像素数据。

2.4 合成和绘制

合成线程将各个图层合成(Composite)到一起,并将其绘制到屏幕上。合成操作在GPU上执行,可以极大地提升性能,特别是在处理滚动和动画时。

三、主线程与合成线程的协作

主线程和合成线程协作完成页面的渲染任务。以下是两者协作的关键点:

3.1 图层的更新

当主线程检测到需要更新图层时,会通知合成线程。合成线程接收到更新信息后,会重新合成图层并绘制到屏幕上。

3.2 图层的同步

主线程和合成线程之间需要频繁同步图层信息,确保合成线程能够准确绘制最新的页面状态。浏览器通过“帧请求”(Frame Request)的机制实现这种同步。

3.3 最小化主线程阻塞

为了优化性能,减少主线程的阻塞时间非常重要。开发者可以通过以下策略实现:

  • 异步加载资源:使用asyncdefer属性异步加载JavaScript文件。
  • 减少重绘和回流:批量操作DOM,避免频繁触发重绘和回流。
  • 利用CSS动画:将动画和过渡效果交给CSS处理,而不是使用JavaScript动画。

四、性能优化策略

理解主线程和合成线程的工作机制后,开发者可以采取以下优化策略:

4.1 优化布局和绘制

  • 减少DOM复杂度:优化HTML结构,减少DOM节点数量。
  • 合并样式计算:将多个样式修改合并为一次操作,减少样式计算次数。
  • 避免大范围重排:尽量避免触发大范围的布局重排(Reflow)。

4.2 利用合成线程

  • 使用will-change:提示浏览器哪些属性会发生变化,提前优化处理。
  • 利用GPU加速:使用CSS属性transformopacity等,利用GPU提升渲染性能。

4.3 优化JavaScript执行

  • 异步加载脚本:使用asyncdefer属性异步加载JavaScript文件,减少阻塞。
  • 减少计算量:优化JavaScript代码,减少复杂计算和频繁的DOM操作。
  • 避免长任务:将长时间执行的任务分解为多个小任务,避免阻塞主线程。

浏览器的渲染进程的线程总共有五种:

image.png

五、总结

浏览器渲染主线程和合成线程在现代浏览器中扮演着重要角色,它们协作完成页面的解析、样式计算、分层、布局、绘制和合成操作。理解这两者的工作机制有助于前端开发者优化页面性能,提高用户体验。

通过合理地利用合成线程、优化主线程的工作负载,以及采用异步加载和GPU加速等技术,开发者可以显著提升页面的渲染性能和交互流畅度。希望本文对您理解浏览器渲染原理有所帮助!如果有任何问题或需要进一步的解释,请随时提出。