在渲染引擎中 DOM 的作用:
- DOM 是生成页面的基础数据结构
- DOM 给 js 提供了操作的接口,从而改变文档的结构样式和内容
-
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 树中间有一段空闲时间,这个时间就是页面渲染的瓶颈
CSSOM 的作用?
- 提供 JavaScript 操作样式的能力
-
合成渲染树涉及的操作
样式计算:渲染引擎回味对应的DOM元素选择样式信息(重绘)
-
白屏的优化策略
因为瓶颈主要体现为下载 CSS 文件,下载 JavaScript 文件和执行 JavaScript 脚本,所以要缩短白屏时长,主要有以下策略:
内联 JavaScript,内联 CSS,以便获取 HTML 文件后直接开始渲染
- 通过 webpack 等工具移除不必要的注释,压缩 JavaScript文件,尽量减少文件的大小
- 在解析阶段为 Script 标签标记 async 和 defer
对于大的 CSS 文件,可以拆分为不同用途的 CSS文件,在特殊场景下加载
显示器成像原理与卡顿
显示器的刷新频率:
每个显示器都有固定刷新频率,通常是 60Hz
- 更新图片都来自显卡中的前缓冲区:按照刷新频率读取前缓冲区的图形,并将图像显示在显示器上
- 显卡的职责就是合成新的图像,并将图像保存到后缓冲区中,一旦显卡把合成的图像写到后缓冲区,系统就会让后缓冲区和前缓冲区互换,这样就能保证显示器能读取到最新显卡合成的图像。
- 有时候,在一些复杂的场景中,显卡处理一张图片的速度会变慢,这样就会造成视觉上的卡顿。
如何生成一帧图像:
通常渲染路径越长,生成图像花费的时间越多
- 重排:会让渲染流水线的每个阶段都执行一遍
- 重绘:没有重新布局的阶段,花费时间相比重排略低
- 合成:操作路径最短,不需要触发布局和绘制两个阶段,如果采用了 GPU,效率会更高。
Chrome 的合成技术,可以用三个词来总结:分层、分块、合成。
分层与合成
为了提升每帧的渲染效率,Chrome 引入了分层和合成的机制。
- 将素材分解为多个图层的操作称为分层
- 将这个图层合到一起的操作称为合成
分层体现在生成布局树之后:
- 渲染引擎会根据布局树的特点将其转换为 Layer Tree
绘制阶段并不是真正的绘制出图片,而是将绘制指令组合成一个列表
有了绘制列表后,就需要进入光栅化阶段了:
- 光栅化就是按照绘制列表中的指令生成图片
- 合成线程将生成的图片发送到缓冲区,这个大概就是分层、合成流程
如何利用分层技术优化代码
-
完整渲染流水线:
构建DOM树
- 样式计算
- 把CSS 转换为 CSSOM
- 转换样式中的属性值,使其标准化
- 计算DOM树中每个节点的具体样式
- 布局阶段
- 构建 render tree
- 布局计算
- 分层
- 对 render tree 进行分层,生成 layer tree
- 绘制
- 为每个图层生成绘制列表,并将其提交给合成线程
- 分块
- 合成线程将图层分成图块,并在光栅化县城中将图块转化成位图
- 光栅化
- 合成
合成线程发送绘制图块命令 DrawQuad 给浏览器进程。
浏览器进程根据 DrawQuad 消息生成页面,并显示到显示器上。