浏览器渲染原理【笔记】

394 阅读5分钟

输入URL后发生了什么

图1.webp

  • 首先,浏览器进程接收到用户输入的 URL 请求,浏览器进程便将该 URL 转发给网络进程。
  • 然后,在网络进程中发起真正的 URL 请求。
  • 接着网络进程接收到了响应头数据,便解析响应头数据,并将数据转发给浏览器进程。
  • 浏览器进程接收到网络进程的响应头数据之后,发送“提交导航 (CommitNavigation)”消息到渲染进程;
  • 渲染进程接收到“提交导航”的消息之后,便开始准备接收 HTML 数据,接收数据的方式是直接和网络进程建立数据管道;
  • 最后渲染进程会向浏览器进程“确认提交”,这是告诉浏览器进程:“已经准备好接受和解析页面数据了”。
  • 浏览器进程接收到渲染进程“提交文档”的消息之后,便开始移除之前旧的文档,然后更新浏览器进程中的页面状态。

Chrome目前的多进程架构

  • 浏览器进程: 主要负责界面显示、用户交互、子进程管理,同时提供存储等功能。
  • 渲染进程: 核心任务是将 HTML、CSS 和 JavaScript 转换为用户可以与之交互的网页,排版引擎 Blink 和 JavaScript 引擎 V8 都是运行在该进程中,默认情况下,Chrome 会为每个 Tab 标签创建一个渲染进程。出于安全考虑,渲染进程都是运行在沙箱模式下。
  • GPU 进程:其实,Chrome 刚开始发布的时候是没有 GPU 进程的。而 GPU 的使用初衷是为了实现 3D CSS 的效果,只是随后网页、Chrome 的 UI 界面都选择采用 GPU 来绘制,这使得 GPU 成为浏览器普遍的需求。最后,Chrome 在其多进程架构上也引入了 GPU 进程。
  • 网络进程:主要负责页面的网络资源加载,开始是作为一个模块运行在浏览器进程里面的,后来独立出来,成为一个单独的进程。
  • 插件进程: 主要是负责插件的运行,因插件易崩溃,所以需要通过插件进程来隔离,以保证插件进程崩溃不会对浏览器和页面造成影响。

复杂的渲染进程

按照渲染的时间顺序,整个渲染可以分为下面几个阶段:

1、构建DOM树

图2.webp

可以在浏览器中输入document更直观的查看DOM树的结构。

2、样式计算(Recalculate Style)

样式计算大体包含三个步骤:

  • 把CSS文件解析成浏览器可理解的结构—styleSheets,浏览器中输入document.styleSheets可以查看
  • 转换样式表中的标准值,使其标准化。例如把rem转换成px。
  • 根据CSS的继承规则层叠规则计算出每个DOM节点的具体样式,最终输出的内容保存在元素的ComputedStyle属性中。

3、布局

布局大概分为两个阶段:

  • 遍历DOM树中的可见节点,创建布局树

图3.webp

  • 进行布局计算(后面单独解释)

4、分层

因为页面中有很多复杂的效果,浏览器为了方便实现这些效果,会对像PhotoShop一次样对页面进行分层,并生成一棵图层树(LayerTree),具体的分层规则可以参考[MDN层叠上下文](developer.mozilla.org/zh-CN/docs/…

5、图层绘制

图层绘制是通过一条条简单的指令完成的,在Chrome控制台中可以打开Layer标签查看页面中的具体指令。例如下图是百度翻译的绘制指令:

图4.png

6、栅格化(raster)操作

栅格化会在合成线程上进行,简单来说就是把图层划分成多个图块(tile),通常是256 x 256或者512 x 512。然后将其转化为位图。具体流程如图:

图5.webp

通常光栅化会在GPU中进行,也叫快速光栅化。

7、合成和显示

光栅化完成后,和合成线程会向浏览器进程发送一个DrawQuad命令,浏览器进程收到命令后会将页面内容绘制到内存中,最后将内存显示在屏幕上。

总结一下

图6.webp

  1. 根据HTML内容生成DOM树
  2. 根据CSS样式表生成styleSheets,计算DOM节点样式
  3. 生成布局树,计算布局信息
  4. 对布局进行分层,生成分层树
  5. 为每个图层生成绘制列表,并将其提交到合成线程
  6. 合成线程将图层分块,并通过光栅化将其转化为位图
  7. 合成线程发送DrawQuad命令给浏览器进程
  8. 浏览器进程根据DrawQuad消息生成页面,并将其显示到屏幕上

根据上面的流程分析下重排重绘布局的性能:

  • 重排:诸如删除元素、改变元素宽高等影响页面布局的行为会发生重排。它需要完整更新上面8个步骤,因此相比之下重排的开销最高。
  • 重绘:诸如改变元素颜色等行为会发生重绘。因为它不会改变元素的几何位置,所以省去了布局和分层阶段(上面步骤的1-4),所以开销比重排要小。
  • 合成:有些操作既不需要修改布局属性,也不需要修改绘制属性,例如CSS的transform属性。直接在合成线程上执行合成动画即可。它既不阻塞主线程,又不需要布局和绘制,所以开销很小。