两个视角看【经典问题:浏览器输入 url 回车后,发生了什么?】(附图)

201 阅读5分钟

前言

作为前端开发者,我们经常需要优化页面加载时间和渲染性能。这不仅涉及到代码层面的优化,还需要深入理解浏览器是如何工作的。

浏览器多进程架构.png

下面将从渲染进程线程两个角度出发,深入探究从输入URL到页面完全呈现在用户面前,浏览器背后所经历的一系列复杂流程。

对齐颗粒度——名称的统一(以 MDN 上的名称为主)

  • CSSOM(CSS Object Model —— CSS 对象模型)树=== CSS 规则树(CSS Rules tree)

  • 渲染树(render tree)=== 布局树(layout tree)

  • 回流(reflow)=== 重排(relayout) 浏览器渲染是基于“流式布局”的模型,所以重排也叫回流

首次:布局(layout),绘制(paint)。

后续:回流(reflow),重绘(repaint)。

  • 渲染树是在布局阶段生成的?还是 cssom 树和 dom 树先构建成渲染树,再进行布局?

先构建完成渲染树,再进行布局阶段。

经典问题:浏览器输入 url 回车后,发生了什么?

渲染视角

【渲染角度】看浏览器输入 URL 回车后的流程.png

  1. 用户输入 URL 回车后,浏览器首先进行 DNS 查询 IP 地址,然后建立 TCP 连接,发送 HTTP 请求,获取 HTML 文件。

  2. 获取到 HTML 后,经过词法解析:将内容拆解成多个独立的 token,如起始标记(<div>)、结束标记(</div>)、属性名称(id)和属性值(myDiv)。语法解析:基于生成的 token,按照 HTML 语法规则构建 DOM 树。

  3. 同时与 HTML 并行,浏览器还会下载并解析 CSS 文件,构建 CSSOM 树。

  4. 结合 DOM 树和 CSSOM 树,浏览器构建渲染树,确定哪些元素需要显示以及样式。

  5. 浏览器进行布局计算,确定每个元素在页面上的位置和尺寸。

  6. 布局完成后,进行分层,确定元素放置哪一层。

  7. 进入绘制阶段,①遍历渲染树来创建绘制记录,②通过获得的每个元素样式、绘制记录等信息,将这些信息转换为屏幕上的像素,这个过程被称为光栅化

  8. 最后,通过合成过程,将每个图层被栅格化后的图块(tiles),经由 GPU 展示在屏幕上。

进程线程视角

【进程线程角度】看浏览器输入 URL 回车后的流程.drawio.png

浏览器渲染的本质是多进程协作和线程调度的结果,各进程通过 IPC(跨进程通信) 实现高效协同。下面从进程和线程的视角,对上面渲染视角进行补充。

  1. 浏览器进程 UI线程: 接收 URL 输入后,触发 IPC通信 与 网络进程 交互。若本地 DNS 缓存未命中,通过系统 DNS 解析器递归查询 IP 地址。
  1. 网络进程: 建立 TCP 连接(三次握手)后,通过HTTP/HTTPS协议传输 HTML 文件。当收到 HTTP 响应时,会触发预加载扫描器(Preload Scanner),来预测和提前加载资源,减少加载时间。
  1. 渲染进程 GUI渲染线程: 进行 HTML、CSS 解析,布局和绘制等任务。
  1. 渲染进程 合成器线程: 生成多个合成图块工作线程(提高渲染效率),将页面拆分为图层(Layer)并分块(256x256像素)。
  1. 渲染进程 光栅线程: 线程池优先光栅化可见区域图块,转换为 GPU 可识别的位图,并上传到 GPU。
  1. GPU进程: 接收合成器线程的位图数据,转换为纹理(GPU中用于存储图像数据的一种结构)储存。
  1. 渲染进程 合成器线程: 使用纹理数据来构建合成帧,准备显示。
  1. GPU进程: 执行渲染操作,将帧内容绘制到帧缓冲区。
  1. 显示到屏幕
硬件加速到底为什么快?

光栅化分两种:软件光栅化(software rasterization)和 硬件光栅化(hardware rasterization)。

  • 软件光栅化:完全依赖 CPU 单线程进行计算,通过算法将矢量图形转换为位图像素数据,上传到 GPU 内存中。(也就是上图中的光栅化流程)

  • 硬件光栅化:利用 GPU 内置的 固定光栅化单元(Hardwired Rasterizer)和 并行处理能力,直接处理几何图形,转换的像素数据直接写入到 GPU 内存中(省去 CPU→GPU 的数据搬运耗时)。

​固定光栅化单元(Hardwired Rasterizer)​​ —— 这是一个直接用物理电路实现光栅化算法的模块(比如三角形覆盖测试、深度插值),类似“工厂流水线的机械臂”,专为特定任务设计,无需软件调度,直接以电路速度运行。

合成器线程的知识点补充
  • 准备画下一帧前,显示器会发出一个垂直同步信号(VSync——vertical synchronization),用于协调帧的绘制节奏。

  • 合成器线程 需要等待 VSync 到来后,才会将完成的 合成帧 提交给 GPU 进行显示,这一步骤确保了帧的更新与显示器的刷新周期一致,避免画面撕裂。

  • 由于执行 JS 是主线程的工作,当页面合成时,合成器线程 会标记页面中绑定有事件监听的区域为 非快速滚动区域(non-fast scrollable region)。

  • 如何判断点击的区域?:通过 命中测试(hit test)来查找对应的事件目标,命中测试会基于渲染过程中生成的绘制记录( paint records )查找事件发生坐标下存在的元素。

  • 事件发生在 非快速滚动区域 时,需等待主线程完成事件处理,可能影响合成流畅性。否则 合成器线程 可以直接合成新的帧而不用等到主线程的响应。

  • 监听相关事件时,传递 passive: true(IE不支持)参数。这样告诉渲染线程,依然需要将事件发送给主线程处理,但不需要等待。

参考链接:

前端文摘:深入解析浏览器的幕后工作原理

图解浏览器的基本工作原理

Chrome 中的 GPU 加速合成

渲染页面:浏览器的工作原理

关键渲染路径

The Anatomy of a Frame