从输入 url 到页面上都发生了什么
- 浏览器进程,处理输入信息(譬如拼接协议头等)
- 浏览器进程,开始导航
- 网络进程,开始发起 URL 请求
- 网络进程,处理响应头,并根据 content-type 判断接下来的操作,如果是 “text/html” 的话,通知浏览器进程,然后继续下载响应内容
- 浏览器进程,准备渲染进程(一般情况下,chrome 会给每个页面开辟一个独立的渲染进程,除非是 same-site)
- 渲染进程,接收到浏览器进程的“提交文档”指令,渲染进程会和网络进程建立连接,并通知浏览器进程“确认提交”
- 浏览器进程,更新浏览器界面状态(包括了安全状态、地址栏的 URL、前进后退的历史状态,并更新 Web 页面)
- 渲染进程,开始解析并渲染页面
事件循环
事件循环(even loop) 其实就是将各种需要被执行的 任务(Task) 一个个单线程的
中可能会有一个或多个任务队列,这些队列分别为了处理:
- 鼠标和键盘事件
- 其他的一些 Task
- 普通任务队列(其实可以理解为宏任务)
- 微任务队列
浏览器会在保持任务顺序的前提下,可能分配四分之三的优先权给鼠标和键盘事件,保证用户的输入得到最高优先级的响应,而剩下的优先级交给其他 Task,并且保证不会“饿死”它们。
对于每一帧中,渲染进程的工作流程就是:
- 响应事件
- 取一个宏任务(
macro)出来执行 - 清空微任务(
micro)队列 - 判断当前帧是否值得更新,否则直接跳到 10
- 如果窗口的大小发生了变化,执行监听的
resize方法 - 如果页面发生了滚动,执行
scroll方法 - 触发
requestAnimationFrame回调 - 触发
IntersectionObserver回调 Frame CommitUI 更新,执行 UI Render- 如果当前空闲(也就是
macro 队列还有micro 队列都为空),则触发requsetIldeCallback回调
浏览器渲染流水线
- 构建 DOM 树
- 渲染进程将 HTML 内容转换为能够读懂的DOM 树结构
- 样式计算(Recalculate Style)
- 把 CSS 转换为浏览器能够理解的结构,并转换样式表中的属性值,使其标准化,即 StyleSheets
- 计算出 DOM 树中每个节点的具体样式
- 布局阶段(Layout)
- 创建布局树
- 计算元素的布局
- 分层(Layer)
- 对布局树进行分层
- 生成分层树
- 绘制(Paint)
- 为每个图层生成绘制列表
- 提交到合成线程
- 合成与显示(Composite Layers)
- 分块(ties),合成线程将图层分成图块
- 栅格化(raster),在光栅化线程池中将图块转换成位图
- 发送绘制图块命令 DrawQuad 给浏览器进程
- 浏览器进程根据 DrawQuad 消息生成页面,并显示到显示器上
1、2、3、4、5 是在渲染进程的主线程中执行的
6 是在渲染进程的合成线程中执行的
7 是在浏览器进程中执行的
更新 DOM 或者 CSS 会导致以下其中一种情况的发生:
- 重排:更新了元素的几何属性,重新执行第 2 步,然后继续剩下所有步骤
- 重绘:更新元素的绘制属性,重新执行第 2 步,然后跳过第 3、4 步,然后继续剩下的步骤
- 合成:直接在非主线程上执行合成动画操作,重新执行第 2 步,然后跳过第 3、4、5 步,然后继续剩下的步骤