在渲染引擎中 DOM 的作用:

  • DOM 是生成页面的基础数据结构
  • DOM 给 js 提供了操作的接口,从而改变文档的结构样式和内容
  • 一些不安全的内容在DOM 解析阶段可以被拒之门外

    DOM 是如何生成的?

    HTML 解析器什么时候开始解析?
    HTML 解析器并不是等文档全部加载完才开始解析,而是网络京城加载了多少数据就解析多少。
    具体的解析流程:

  • 如果 content-type 是 text/html ,那么就会为该请求创建一个渲染进程

  • 网络进程会和渲染进程建立一个消息通道,将请求到的字节流传输到渲染进程
  • 渲染进程通过 HTML 解析器处理字节流,HTML 解析器开始工作时,会创建一个根为 document 的空 DOM 结构
  • 字节流的转换需要三个阶段:
    • 通过分词器将字节流转换为 Token,Token 分为 Tag Token 和文本 Token
    • 后两个阶段是同步进行的,将 Token 解析为 DOM 节点,并将该节点添加到 DOM 树中
  • 当解析到 Script 标签时,HTML 解析器会暂停 DOM 解析,浏览器做了预解析操作来优化这一问题
  • 如果在请求时遇到了 JavaScript 和 CSS 文件,那么预解析线程就会提前下载这些数据。
    • 这两个文件的下载过程是重叠的,所以下载时间按照最久的那个计算
    • 不管这两个文件谁先到达,都先等 CSS 文件下载完并生成 CSSOM,然后在执行 js 脚本,构建 DOM 和布局树
  • 因为 JavaScript 脚本会阻塞 DOM 生成,而 CSS 文件又会阻塞 JavaScript 的执行,为了避免这一问题,脚本又添加了 async 属性和 defer 属性
  • 请求 HTML数据和构建 DOM 树中间有一段空闲时间,这个时间就是页面渲染的瓶颈

image.png

CSSOM 的作用?

  • 提供 JavaScript 操作样式的能力
  • 为布局树的合成提供了基础样式的信息

    合成渲染树涉及的操作

  • 样式计算:渲染引擎回味对应的DOM元素选择样式信息(重绘)

  • 计算布局:计算布局树中的每个样式的几何位置(重排)

    白屏的优化策略

    因为瓶颈主要体现为下载 CSS 文件,下载 JavaScript 文件和执行 JavaScript 脚本,所以要缩短白屏时长,主要有以下策略:

  • 内联 JavaScript,内联 CSS,以便获取 HTML 文件后直接开始渲染

  • 通过 webpack 等工具移除不必要的注释,压缩 JavaScript文件,尽量减少文件的大小
  • 在解析阶段为 Script 标签标记 async 和 defer
  • 对于大的 CSS 文件,可以拆分为不同用途的 CSS文件,在特殊场景下加载

    显示器成像原理与卡顿

    显示器的刷新频率:

  • 每个显示器都有固定刷新频率,通常是 60Hz

  • 更新图片都来自显卡中的前缓冲区:按照刷新频率读取前缓冲区的图形,并将图像显示在显示器上
  • 显卡的职责就是合成新的图像,并将图像保存到后缓冲区中,一旦显卡把合成的图像写到后缓冲区,系统就会让后缓冲区和前缓冲区互换,这样就能保证显示器能读取到最新显卡合成的图像。
  • 有时候,在一些复杂的场景中,显卡处理一张图片的速度会变慢,这样就会造成视觉上的卡顿。

如何生成一帧图像:

通常渲染路径越长,生成图像花费的时间越多

  • 重排:会让渲染流水线的每个阶段都执行一遍
  • 重绘:没有重新布局的阶段,花费时间相比重排略低
  • 合成:操作路径最短,不需要触发布局和绘制两个阶段,如果采用了 GPU,效率会更高。

Chrome 的合成技术,可以用三个词来总结:分层、分块、合成。

分层与合成

为了提升每帧的渲染效率,Chrome 引入了分层和合成的机制。

  • 将素材分解为多个图层的操作称为分层
  • 将这个图层合到一起的操作称为合成

分层体现在生成布局树之后:

  • 渲染引擎会根据布局树的特点将其转换为 Layer Tree

绘制阶段并不是真正的绘制出图片,而是将绘制指令组合成一个列表
有了绘制列表后,就需要进入光栅化阶段了:

  • 光栅化就是按照绘制列表中的指令生成图片
  • 合成线程将生成的图片发送到缓冲区,这个大概就是分层、合成流程

如何利用分层技术优化代码

  • will-change

    完整渲染流水线:

  • 构建DOM树

  • 样式计算
    • 把CSS 转换为 CSSOM
    • 转换样式中的属性值,使其标准化
    • 计算DOM树中每个节点的具体样式
  • 布局阶段
    • 构建 render tree
    • 布局计算
  • 分层
    • 对 render tree 进行分层,生成 layer tree
  • 绘制
    • 为每个图层生成绘制列表,并将其提交给合成线程
  • 分块
    • 合成线程将图层分成图块,并在光栅化县城中将图块转化成位图
  • 光栅化
  • 合成

合成线程发送绘制图块命令 DrawQuad 给浏览器进程。
浏览器进程根据 DrawQuad 消息生成页面,并显示到显示器上。
image.png