先说点题外话,上面这种midjourney+ chatgpt生成的图效果还挺不错的。言归正传,由于目前基本没有系统介绍浏览器知识的文档,而且网上看到的很多相关文档都是比较早期的,很多内容都不太适合新版的浏览器了。 这里将浏览器知识和前端系统下结合起来讲一下这个经典的面试题。
整体流程
- 用户输入
URL请求- 准备渲染进程
- 提交文档
- 渲染流水线
用户输入
-
用户在地址栏按下回车,检查输入(关键字 or 符合 URL 规则),组装完整 URL;
-
回车前,当前页面执行 onbeforeunload 事件;
beforeunload 事件允许页面在退出之前执行一些数据清理操作,还可以询问用户是否要离开当前页面,比如当前页面可能有未提交完成的表单等情况,因此用户可以通过 beforeunload 事件来取消导航,让浏览器不再执行任何后续工作。
- 浏览器进入加载状态。
URL请求
-
浏览器进程通过
IPC把URL请求发送至网络进程; -
查找资源缓存(有效期内);
-
DNS解析(查询DNS缓存); -
进入 TCP 队列(单个域名 TCP 连接数量限制);
http1.1,同一个域名同时最多只能建立 6 个TCP连接,多了会进入等待状态。http2.0,是可以并行请求资源的,浏览器只会为每个域名维护一个tcp连接。
-
创建
TCP连接(三次握手); -
HTTPS建立TLS连接(client hello, server hello, pre-master key生成『对话密钥』); -
发送
HTTP请求(请求行[方法、URL、协议]、请求头Cookie等、请求体POST); -
接受请求(响应行[协议、状态码、状态消息]、响应头、响应体等); - 状态码
301 / 302,根据响应头中的Location重定向; - 状态码200,根据响应头中的Content-Type决定如何响应(下载文件、加载资源、渲染HTML)。
准备渲染进程
根据是否同一站点(相同的协议和根域名),决定是否复用渲染进程。
提交文档
-
浏览器进程接受到网路进程的响应头数据,向渲染进程发送『提交文档』消息;
-
渲染进程收到『提交文档』消息后,与网络进程建立传输数据『管道』;
-
传输完成后,渲染进程返回『确认提交』消息给浏览器进程;
-
浏览器接受『确认提交』消息后,移除旧文档、更新界面、地址栏,导航历史状态等;
-
此时标识浏览器加载状态的小圆圈,从此前
URL网络请求时的逆时针选择,即将变成顺时针旋转(进入渲染阶 段)。
渲染流水线
- 构建
DOM树
-
输入:
HTML文档; -
处理:
HTML解析器解析; -
输出:
DOM数据解构。
- 样式计算
从图中可以看出,
CSS 样式来源主要有三种:通过 link 引用的外部 CSS 文件
-
输入:
CSS文本; -
处理:属性值标准化,每个节点具体样式(继承、层叠);
-
输出:
styleSheets(CSSOM)。
和 HTML 文件一样,浏览器也是无法直接理解这些纯文本的 CSS 样式,所以当渲染引擎接收到 CSS 文本时,会执行一个转换操作,将 CSS 文本转换为浏览器可以理解的结构——styleSheets。
- 布局(
DOM树中元素的计划位置)
-
DOM&CSSOM合并成渲染树; -
布局树(
DOM树中的可见元素); -
布局计算。
上图就是渲染的一部分步骤了。
- 分层
-
特定节点生成专用图层,生成一棵图层树(层叠上下文、
Clip,类似PS里的图层); -
拥有层叠上下文属性(明确定位属性、透明属性、
CSS滤镜、z-index等)的元素会创建单独图层; -
没有图层的
DOM节点属于父节点图层; -
需要剪裁的地方也会创建图层。
- 绘制指令
-
输入:图层树;
-
渲染引擎对图层树中每个图层进行绘制;
-
拆分成绘制指令,生成绘制列表,提交到合成线程;
-
输出:绘制列表。
- 分块
- 合成线程会将较大、较长的图层(一屏显示不完,大部分不在视口内)划分为图块(tile, 256256, 512512)。
- 光栅化(栅格化)
-
在光栅化线程池中,将视口附近的图块优先生成位图(栅格化执行该操作);
-
快速栅格化:
GPU加速,生成位图(GPU进程)。
- 合成绘制
-
绘制图块命令——
DrawQuad,提交给浏览器进程; -
浏览器进程的
viz组件,根据DrawQuad命令,绘制在屏幕上。
上图就是完整的渲染流程了
相关概念
更新了元素的几何属性(重排)
从上图可以看出,如果你通过
JavaScript 或者 CSS 修改元素的几何位置属性,例如改变元素的宽度、高度等,那么浏览器会触发重新布局,解析之后的一系列子阶段,这个过程就叫重排。无疑,重排需要更新完整的渲染流水线,所以开销也是最大的。
更新元素的绘制属性(重绘)
从图中可以看出,如果修改了元素的背景颜色,那么布局阶段将不会被执行,因为并没有引起几何位置的变换,所以就直接进入了绘制阶段,然后执行之后的一系列子阶段,这个过程就叫重绘。相较于重排操作,重绘省去了布局和分层阶段,所以执行效率会比重排操作要高一些。
直接合成阶段
在上图中,我们使用了 CSS 的 transform 来实现动画效果,这可以避开重排和重绘阶段,直接在非主线程上执行合成动画操作。这样的效率是最高的,因为是在非主线程上合成,并没有占用主线程的资源,另外也避开了布局和绘制两个子阶段,所以相对于重绘和重排,合成能大大提升绘制效率。