浏览器渲染过程

298 阅读3分钟

从输入 url 到页面上都发生了什么

  1. 浏览器进程,处理输入信息(譬如拼接协议头等)
  2. 浏览器进程,开始导航
  3. 网络进程,开始发起 URL 请求
  4. 网络进程,处理响应头,并根据 content-type 判断接下来的操作,如果是 “text/html” 的话,通知浏览器进程,然后继续下载响应内容
  5. 浏览器进程,准备渲染进程(一般情况下,chrome 会给每个页面开辟一个独立的渲染进程,除非是 same-site)
  6. 渲染进程,接收到浏览器进程的“提交文档”指令,渲染进程会和网络进程建立连接,并通知浏览器进程“确认提交”
  7. 浏览器进程,更新浏览器界面状态(包括了安全状态、地址栏的 URL、前进后退的历史状态,并更新 Web 页面)
  8. 渲染进程,开始解析并渲染页面

事件循环

事件循环(even loop) 其实就是将各种需要被执行的 任务(Task) 一个个单线程的

中可能会有一个或多个任务队列,这些队列分别为了处理:

  • 鼠标和键盘事件
  • 其他的一些 Task
    • 普通任务队列(其实可以理解为宏任务)
    • 微任务队列

浏览器会在保持任务顺序的前提下,可能分配四分之三的优先权给鼠标和键盘事件,保证用户的输入得到最高优先级的响应,而剩下的优先级交给其他 Task,并且保证不会“饿死”它们。

对于每一帧中,渲染进程的工作流程就是:

  1. 响应事件
  2. 取一个宏任务(macro)出来执行
  3. 清空微任务(micro)队列
  4. 判断当前帧是否值得更新,否则直接跳到 10
  5. 如果窗口的大小发生了变化,执行监听的 resize 方法
  6. 如果页面发生了滚动,执行 scroll 方法
  7. 触发 requestAnimationFrame 回调
  8. 触发 IntersectionObserver 回调
  9. Frame Commit UI 更新,执行 UI Render
  10. 如果当前空闲(也就是macro 队列还有micro 队列都为空),则触发 requsetIldeCallback 回调

浏览器渲染流水线

  1. 构建 DOM 树
    1. 渲染进程将 HTML 内容转换为能够读懂的DOM 树结构
  2. 样式计算(Recalculate Style)
    1. 把 CSS 转换为浏览器能够理解的结构,并转换样式表中的属性值,使其标准化,即 StyleSheets
    2. 计算出 DOM 树中每个节点的具体样式
  3. 布局阶段(Layout)
    1. 创建布局树
    2. 计算元素的布局
  4. 分层(Layer)
    1. 对布局树进行分层
    2. 生成分层树
  5. 绘制(Paint)
    1. 为每个图层生成绘制列表
    2. 提交到合成线程
  6. 合成与显示(Composite Layers)
    1. 分块(ties),合成线程将图层分成图块
    2. 栅格化(raster),在光栅化线程池中将图块转换成位图
    3. 发送绘制图块命令 DrawQuad 给浏览器进程
  7. 浏览器进程根据 DrawQuad 消息生成页面,并显示到显示器上

1、2、3、4、5 是在渲染进程的主线程中执行的

6 是在渲染进程的合成线程中执行的

7 是在浏览器进程中执行的

更新 DOM 或者 CSS 会导致以下其中一种情况的发生:

  • 重排:更新了元素的几何属性,重新执行第 2 步,然后继续剩下所有步骤
  • 重绘:更新元素的绘制属性,重新执行第 2 步,然后跳过第 3、4 步,然后继续剩下的步骤
  • 合成:直接在非主线程上执行合成动画操作,重新执行第 2 步,然后跳过第 3、4、5 步,然后继续剩下的步骤