渲染流程

参考文章

渲染进程

渲染程序进程负责标签页内发生的一切。 合成器和光栅线程也会在渲染程序进程内运行,以便高效、流畅地渲染页面。 渲染程序进程的核心任务是将 HTML、CSSJavaScript 转换为用户可与之互动的网页。

渲染进程中的主线程会处理大多数工作,例如:

  • 解析文本字符串 (HTML),生成 DOM。
  • 计算样式
  • 布局
  • 处理图层
  • 每秒把⻚⾯画 60 次
  • 执⾏全局 JS 代码
  • 执⾏事件处理函数
  • 执⾏计时器的回调函数

Web WorkerService Worker,则部分 JavaScript 将由工作器线程处理。

渲染流程

DOM构建

为什么需要构建 DOM 树?

因为浏览器无法直接理解和使用 HTML,所以需要将 HTML 转换为浏览器能够理解的结构——DOM 树。

预加载资源

网站通常使用图片、CSSJavaScript 等外部资源。

加快速度,系统会并发运行**“预加载扫描程序”**。如果 HTML 文档中包含 <img><link> 等内容,预加载扫描器会查看由 HTML 解析器生成的 token,并将请求发送到浏览器进程中的网络线程。

js是如何阻止dom树构建

HTML解析器找到 <script> 标记时,它会暂停解析 HTML 文档,并加载、解析并执行 JavaScript 代码。因为 js 可能会修改当前已经生成的dom结构。

不管该脚本是否操纵了 CSSOM,都会执行 CSS 文件下载,解析操作,再执行 JavaScript 脚本。JavaScript 会阻塞 DOM 生成,而样式文件又会阻塞 JavaScript 的执行。

样式计算(Recalculate Style)

样式计算的目的是为了计算出 DOM 节点中每个元素的具体样式,这个阶段大体可分为三步来完成:

  1. 把 CSS 转换为浏览器能够理解的结构——styleSheets。
  2. 转换样式表中的属性值,使其标准化(主要是将指标准化)。
  3. 计算出 DOM 树中每个节点的具体样式(遵守 CSS 的继承层叠两个规则)

布局

布局是查找元素几何形状的过程。主线程会遍历 DOM 和计算出的样式,并创建包含 x y 坐标和边界框大小等信息的布局树。

布局树的结构可能与 DOM 树类似,但它仅包含与页面上可见内容相关的信息。 display: none,则该元素不属于布局树的一部分(但是,具有 visibility: hidden 的元素位于布局树中)。

分层

渲染引擎还需要为特定的节点生成专用的图层,并生成一棵对应的图层树(LayerTree)

那么需要满足什么条件,渲染引擎才会为特定的节点创建新的图层呢?

  • 拥有层叠上下文属性的元素会被提升为单独的一层(position, z-index, filter: blur(5px), opacity
  • 需要剪裁(clip)的地方也会被创建为图层

主线程会将该信息提交到合成器线程。

图层绘制

合成器线程会把一个图层的绘制拆分成很多图块,并将每个图块发送到光栅线程。

光栅化

光栅线程会光栅化每个图块并将其存储在 GPU 内存中。

合成和显示

将图块进行光栅化后,合成器线程会收集称为“绘制四边形”的图块信息,以创建合成器帧。然后通过 IPC 将合成器帧提交到浏览器进程。

合成的优势在于,它在完成时不涉及主线程。合成器线程不需要等待样式计算或 JavaScript 执行。因此,仅合成动画被认为是实现流畅性能的最佳方式。如果需要再次计算布局或绘制,则必须涉及主线程。

总结

  1. 渲染进程将 HTML 生成 DOM树结构
  2. CSS 样式表 转换成 styleSheets
  3. 创建布局树,并且计算布局信息(位置,是否显示)。
  4. 分层,生成图层树,提交给合成线程
  5. 合成线程将图层分为多个图块,发给光栅线程
  6. 光栅线程光栅化每个图块并存于GPU
  7. 合成线程创建合成器帧 通过IPC提交给浏览器进程,界面更改,这些合成器帧会发送到 GPU 以在屏幕上显示。