技术知识文章集合TECHNICAL ARCHIVE · 457 DOCUMENTS

显示模式

登录
ARCHIVE DOCUMENTBR

浏览器工作原理

所属馆藏
Browser
文件格式
Markdown
原始路径
Browser/28-浏览器工作原理_1
本文目录16 个章节
  1. 一、进程、线程和并行处理
  2. 二、从单进程到多进程浏览器
  3. 三、当前架构是固定的吗?
  4. 四、从输入 URL 到导航提交
  5. 五、HTML 解析和资源加载
  6. 六、渲染流水线
  7. 七、图层与合成
  8. 八、回流、重绘和合成
  9. 九、事件循环和一帧
  10. 十、页面生命周期和资源回收
  11. 十一、把原文的 HTTP 八阶段更新为现代链路
  12. 十二、面试题和正确回答
  13. 十三、性能工具如何验证
  14. 十四、原文中需要保留但必须加限定的内容
  15. 总结
  16. 参考资料

浏览器工作原理

Category(分类): Browser, Network, Rendering Status: 已更新

女朋友:浏览器原理为什么要学这么多?

我:因为网络请求、页面渲染、JavaScript 执行、进程线程和 Web 安全并不是互相独立的知识。把它们放到浏览器架构里,可以看到一条完整链路:

输入 URL
  -> 导航与网络
  -> 创建/选择渲染上下文
  -> HTML/CSS/JavaScript 解析
  -> 样式、布局、绘制、合成
  -> 用户看到并交互的页面

原文以 Chrome/Chromium 为主要例子,并混合了浏览器进程、HTTP 请求、导航流程和渲染流水线。Chrome、Edge 等 Chromium 浏览器具有代表性,但浏览器内部进程、线程和调度属于实现细节,不应该把某个版本的架构图当成所有浏览器的永久规则。

一、进程、线程和并行处理

1. 进程是什么

进程是程序运行时的资源和隔离边界,通常拥有自己的地址空间、代码、数据和至少一个线程。操作系统可以调度多个进程并行或交错执行。

2. 线程是什么

线程是进程中的执行单元,同一进程的线程通常共享部分内存和资源,因此通信快,但也需要处理并发访问、竞态和同步问题。线程不能脱离进程单独作为普通应用资源存在。

原文用下面的任务说明并行:

A = 1 + 2
B = 20 / 5
C = 7 * 8
显示 A、B、C

如果 A、B、C 彼此独立,多个线程或 Worker 可能并行计算;但创建线程、传递数据、同步和调度也有成本,并不是线程越多越快。

3. 浏览器为什么需要隔离

浏览器同时运行:

  • 浏览器界面和标签页管理;
  • 多个网站的 JavaScript 和 DOM;
  • 网络、缓存、存储和下载;
  • 图片、视频、字体和 GPU 绘制;
  • 扩展、插件和开发者工具。

如果所有功能在同一个进程中,一个页面的无限循环、渲染崩溃或插件错误可能影响整个浏览器。多进程带来稳定性、性能隔离和安全沙箱,但也增加内存和进程间通信成本。

二、从单进程到多进程浏览器

1. 单进程时代

早期浏览器可能把网络、渲染、脚本、插件和 UI 放在一个进程中。

单进程浏览器架构示意

优点是模块之间通信简单,缺点是:

  • 一个模块崩溃可能拖垮整个浏览器;
  • 一个页面的长脚本可能阻塞其他页面和浏览器 UI;
  • 所有网站共享更多高权限资源,安全边界更弱;
  • 资源泄漏不容易通过关闭单个页面回收。

2. 多进程时代

现代 Chromium 常见的高层组件包括:

  • Browser process:地址栏、标签页、窗口、浏览器 UI、子进程管理和导航协调;
  • Renderer process:Blink、V8、DOM、样式、布局和页面脚本;通常按站点/标签页等策略分配;
  • Viz/GPU 相关进程:聚合多个渲染上下文并完成栅格化、绘制或 GPU 协作;
  • Network/服务进程:网络请求、缓存、代理、DNS 等,具体是否是独立进程随版本和平台变化;
  • Utility process:音视频解码、数据处理等隔离服务;
  • Extension/插件相关进程:扩展和兼容插件的隔离运行环境。

早期多进程浏览器架构示意

现代 Chromium 进程架构示意

进程间通过 IPC 或浏览器内部消息机制协作。一个页面进程崩溃时,浏览器可以显示“页面崩溃”,而不必让全部浏览器退出;但浏览器主进程、GPU/Viz、操作系统内存压力和共享服务出现问题时,影响范围仍可能扩大。

3. 安全沙箱

渲染进程通常在沙箱中运行,限制网页代码直接访问操作系统敏感资源。沙箱不是“网页绝对无法攻击系统”的保证:

  • 浏览器和操作系统仍需要持续修复漏洞;
  • 权限更高的浏览器进程和扩展会影响安全边界;
  • 网站自身的 XSS、供应链和错误消息权限仍然危险;
  • 沙箱限制的是进程能力,不是对 DOM、Cookie 或同源数据的业务授权。

因此要组合使用进程隔离、沙箱、同源策略、Site Isolation、CSP、权限策略和安全更新。

4. Site Isolation 和进程分配

“一个标签页一个渲染进程”是过于绝对的旧说法。现代浏览器会综合站点隔离、是否由另一个页面打开、iframe 的站点、设备内存和浏览器策略决定:

  • 不同站点的跨站 iframe 通常隔离到不同渲染进程,以保护站点之间的性能和安全;
  • 同一站点的页面可能因为 opener 关系或内存策略共享进程,也可能各自使用进程;
  • 同源、同站点、同一进程是三个不同概念;
  • 进程分配可随浏览器版本、平台、内存压力和 WebView 宿主变化。

原文提到的 process-per-site-instance 是 Chromium 历史/实现策略名,不能作为开发者用来预测所有页面进程的 API。

三、当前架构是固定的吗?

不是。Chromium RenderingNG 文档描述了渲染任务如何拆分到主线程、合成线程、Viz 和辅助线程;不同平台可能采用 CPU 回退、不同 GPU 路径或不同进程结构。

“面向服务的架构(Services Oriented Architecture)”可以保留作为浏览器架构演化方向:把 UI、网络、存储、文件、设备等能力拆成可复用的服务,按平台和资源情况组合。但它是内部架构方向,不是网页代码可以直接依赖的固定进程清单。

四、从输入 URL 到导航提交

从输入 URL 到导航提交的流程示意

下面以输入 https://example.com/ 为例。实际浏览器会并行预连接、读取缓存、处理扩展和安全策略,以下是便于学习的主路径。

1. 地址栏处理

浏览器先判断输入内容是 URL 还是搜索关键词:

  • URL 缺少协议时,浏览器可能补全或使用默认搜索引擎;
  • 完整 URL 包含 scheme、host、port、path、query 和 fragment;
  • 用户回车后,地址栏显示加载状态,但旧页面可能继续显示,直到新导航提交。

beforeunload 可以在存在未保存内容时提示用户,但不应滥用。现代浏览器限制自定义提示文本,也可能不在所有场景触发;更可靠的是自动保存草稿和使用 visibilitychange 处理页面离开。

2. 发起导航

浏览器进程协调导航,网络服务根据 URL 和策略发起请求。是否复用当前渲染进程、是否创建新进程、是否需要下载管理器,会在导航过程中决定。

3. 查找缓存

浏览器可能命中 HTTP 缓存、Service Worker、预加载资源或 BFCache。命中 BFCache 时可能直接恢复历史页面快照,并非重新加载 HTML。

缓存未命中或需要验证时,才进入 DNS、连接、TLS 和 HTTP 请求流程。缓存的详细规则见前端缓存

4. DNS、连接和安全协议

浏览器可能先查找浏览器/操作系统/本地解析器缓存,再通过 DNS 获取地址。实际可能使用 DNS over HTTPS、DNS over TLS、代理、本地 hosts 或预解析。

连接协议取决于服务器和客户端协商:

  • HTTP/1.1 通常通过 TCP;
  • HTTP/2 通过 TLS 上的 TCP,并在一条连接上复用多个流;
  • HTTP/3 使用 QUIC/UDP;
  • HTTPS 还要完成 TLS 握手和证书校验。

不要再把“同一个域名最多 6 个 TCP 连接”作为现代浏览器的固定规则。连接数和并发流受 HTTP 版本、代理、网络地址、连接复用、优先级、浏览器实现和服务器配置影响。

默认端口通常是:

http  -> 80
https -> 443

如果 URL 显式写了端口,则使用显式端口。

5. HTTP 请求

HTTP/1.1 请求大致包括:

GET / HTTP/1.1
Host: example.com
Accept: text/html
Accept-Encoding: br, gzip
Connection: keep-alive

浏览器还可能附加 Cookie、缓存验证器、语言、客户端提示和安全相关请求头。HTTPS 中的 HTTP 内容会在 TLS 连接内传输,网络中间人不能直接读取明文请求体。

HTTP/2/3 不再以完全相同的文本格式在网络上传输,但请求语义仍包含方法、目标、头和可选请求体。

6. 重定向

服务器可能返回:

HTTP/1.1 301 Moved Permanently
Location: https://www.example.com/

浏览器读取 Location 后继续导航。重定向会增加延迟,还可能改变方法、缓存和安全上下文。服务端应减少不必要的链式重定向,并校验开放重定向参数。

301308 通常表达永久重定向,302303307 有不同的方法保留语义,不应统称为“301 都一样”。

7. 服务端响应

响应包括状态行、响应头和响应体:

HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Cache-Control: no-cache
Content-Length: 1234

<!doctype html>

Content-Type 会影响浏览器处理方式:

  • text/html 通常交给文档导航和渲染流程;
  • application/javascript 等脚本资源交给脚本加载器;
  • application/octet-stream 常被当作下载内容,但最终仍受 Content-Disposition、浏览器策略和文件类型影响;
  • MIME 配置错误可能造成页面下载、脚本拒绝执行或安全问题。

8. Commit Navigation

网络服务得到响应头后,浏览器进程根据安全策略、响应类型、站点隔离和渲染进程状态,向目标渲染进程发送导航提交信息。渲染进程与网络服务建立数据管道,接收 HTML 字节流。

渲染进程确认能够接收并处理文档后,浏览器更新地址栏、历史记录、安全状态和加载状态。此时页面可能先是空白或部分内容,之后才进行 HTML 解析和子资源加载。

五、HTML 解析和资源加载

1. 字节到 DOM

HTML 的大致处理过程:

字节 -> 字符解码 -> 令牌化 -> 节点 -> DOM
<!doctype html>
<html lang="zh-CN">
  <head>
    <meta charset="utf-8">
    <link rel="stylesheet" href="/app.css">
    <script type="module" src="/app.js"></script>
  </head>
  <body>
    <main><h1>Hello</h1></main>
  </body>
</html>

HTML 解析器不只是简单地按字符串分割标签,还要处理错误恢复、脚本插入、表格规则和自定义元素等 HTML 标准行为。

2. 预加载扫描器

浏览器通常会在主 HTML 解析之外扫描即将加载的资源,提前发现 CSS、脚本、图片和字体。这是实现细节,但能解释为什么某些资源在 DOM 尚未构建完成时已经开始请求。

3. 脚本对解析的影响

经典脚本默认会暂停 HTML 解析并执行:

<script src="/app.js"></script>

更现代的选择:

<script src="/analytics.js" async></script>
<script src="/app.js" defer></script>
<script type="module" src="/entry.js"></script>
  • async 下载不阻塞解析,下载完成后尽快执行,多个脚本顺序不保证;
  • defer 在 HTML 解析完成后执行,经典脚本之间保持顺序;
  • 模块脚本默认延迟执行,并按依赖图加载;
  • 执行时间过长仍会阻塞主线程、交互和渲染。

4. CSS 对脚本和渲染的影响

样式表通常是渲染阻塞资源,浏览器需要样式信息才能安全绘制。CSS 不一定让 HTML 解析在所有情况下完全停止,但位于样式表之后的经典脚本可能等待样式表,因为脚本可能读取计算样式:

<link rel="stylesheet" href="/app.css">
<script>
  console.log(getComputedStyle(document.body).color)
</script>

不要用 setTimeout 推断 CSS 是否阻塞解析;要结合脚本位置、脚本类型、样式依赖和 DevTools 实测。

六、渲染流水线

现代 Chromium 的内部流水线比“DOM + CSSOM -> Render Tree -> Layout -> Paint -> GPU”更细。可以用下面的学习模型:

  1. Style:根据 CSS 规则生成计算样式;
  2. Layout:计算元素尺寸和位置,生成布局片段/布局树;
  3. Pre-paint:准备属性树、裁剪和绘制失效;
  4. Paint:生成描述文本、背景、边框和阴影的显示列表;
  5. Layerize:根据需要组织合成层;
  6. Raster:把绘制列表、图片和字体等栅格化为纹理/位图;
  7. Activate/Composite:合成视觉效果、滚动和各层;
  8. Aggregate/Draw:由 Viz/GPU 等组件聚合并绘制到屏幕。

浏览器渲染流水线示意

实际阶段可以跳过:例如一个满足条件的 transform 动画可能不需要重新布局和绘制;但“所有 transform 都只走 GPU”是不准确的。

1. DOM 和计算样式

DOM 是文档结构;CSSOM 是浏览器提供的 CSS 对象和规则模型,document.styleSheets 只是访问其中一部分的接口。计算样式可以通过:

const style = getComputedStyle(document.querySelector('.card'))
console.log(style.display, style.width, style.color)

计算样式受继承、层叠、选择器匹配、媒体查询、变量、字体和用户代理样式影响。red2em 等值会在计算/使用过程中解析为适合布局和绘制的值,但不要把所有内部中间结构都简化成一个公开的“CSSOM 树”。

2. 布局树

布局阶段需要知道可见盒子的几何信息:

  • display: none 的节点通常不参与布局;
  • headmeta 等不产生可见布局盒;
  • visibility: hidden 通常仍占据布局空间;
  • 伪元素、匿名盒、列表标记和 iframe 可能产生额外布局对象;
  • 一个节点的尺寸变化可能影响父节点、兄弟节点和后代节点。
DOM + computed style -> layout boxes/fragments -> size/position

“渲染树”是历史教学术语;现代引擎内部可能使用 Layout Tree、fragment tree 和 property trees 等不同结构。

布局阶段示意

3. 绘制和栅格化

绘制阶段不是直接把每个 DOM 节点变成屏幕像素,而是生成按顺序排列的绘制指令/显示列表,例如:

绘制背景 -> 边框 -> 文本 -> 阴影 -> 图片

绘制列表示意

之后浏览器把绘制列表划分为适合处理的区域或 tile,再进行栅格化。

图块栅格化示意

栅格化可能使用 GPU,也可能因平台、驱动、内容和资源情况回退到 CPU。GPU 是实现优化的一部分,不是网页开发者可以强制打开的万能开关。

七、图层与合成

布局对象、合成层和绘制列表示意

浏览器不会为每个 DOM 节点创建独立图层。图层化由合成需求和浏览器启发式算法决定,可能受以下因素影响:

  • transform、opacity、filter 等视觉效果;
  • 视频、canvas、滚动容器和固定定位;
  • 3D 变换和动画;
  • 裁剪、遮罩、混合和层叠关系;
  • will-change 提示;
  • 浏览器平台和内存预算。

层叠上下文不等于合成层。positionz-indexopacitytransform 等可以创建层叠上下文,但不代表一定被提升为独立合成层。

.animated {
  will-change: transform;
}

.animated.is-idle {
  will-change: auto;
}

will-change 是提前提示,长期对大量元素使用会增加内存和栅格化成本。原文建议到处使用 translateZ(0) 强制 GPU,这种做法不应作为现代通用方案。

八、回流、重绘和合成

1. 布局更新(Reflow/Layout)

修改会影响几何尺寸、位置或文档流的内容,可能触发布局:

  • 宽高、内外边距、边框、字体和文本内容;
  • 添加、删除和移动节点;
  • 改变容器尺寸或布局模式。

布局范围取决于布局模型和失效范围,不是每次都重算整棵 DOM 树。

2. 重绘(Paint)

颜色、背景、阴影等不影响几何的变化可能只需要重新绘制,但仍可能有较高成本,尤其是大面积阴影、滤镜和复杂文本。

3. 合成(Composite)

如果属性满足合成条件,浏览器可能只更新合成属性,例如某些 transformopacity 动画:

.panel {
  transition: transform 200ms ease, opacity 200ms ease;
}

这通常比反复修改 topleft 更容易避免布局,但不是保证。大型图层、透明重叠、滤镜和栅格化仍可能造成内存或 GPU 压力。

4. 强制同步布局

读取几何属性不会无条件触发布局;当之前有尚未处理的 DOM/CSS 写入,而读取必须得到最新布局时,浏览器可能强制同步布局:

// 容易形成读写交替
for (const item of items) {
  item.style.width = `${container.offsetWidth}px`
}

// 先集中读取,再集中写入
const width = container.offsetWidth
for (const item of items) {
  item.style.width = `${width}px`
}

常见可能触发布局的读取包括 offsetWidthclientHeightscrollTopgetBoundingClientRect() 和部分 getComputedStyle() 使用。是否真的强制布局取决于样式是否失效、读取属性和浏览器实现。

5. 现代优化

  • 使用 requestAnimationFrame 安排视觉写入;
  • 批量读写,避免读写交替;
  • 对滚动、resize、输入和 pointermove 做有意义的节流;
  • 动画优先考虑 transform/opacity,但用 Performance 面板验证;
  • 对大量列表使用虚拟化、分页或 content-visibility: auto
  • 图片预留尺寸,减少 CLS;
  • 把复杂计算放进 Worker,不要把 Worker 当成 DOM 并行修改工具。

九、事件循环和一帧

页面 JavaScript 与许多 DOM、样式和布局任务共享渲染主线程。一个简化的帧处理过程可能是:

执行 task
  -> 清空 microtasks
  -> 运行合适的 requestAnimationFrame 回调
  -> 样式/布局/绘制/合成
  -> 下一次渲染机会

但浏览器可以跳过渲染、合并任务、节流后台页面,也可以让滚动和部分动画在合成线程继续运行。不要把 60Hz、16.67ms 和“每轮必渲染”当成绝对规则。

requestAnimationFrame(() => {
  element.style.transform = `translateX(${nextX}px)`
})

如果一个同步任务超过几十毫秒,即使后续 CSS 能合成,主线程也可能无法及时处理输入和提交更新。性能问题要结合 Long Task、INP、帧图和渲染流水线判断。

十、页面生命周期和资源回收

关闭标签页后,浏览器可能回收对应渲染进程和资源,但不能把它当成解决内存泄漏的唯一方法:

  • SPA 在不离开页面时可能长期积累监听器、定时器和缓存引用;
  • Worker、WebSocket、Observer 和 IndexedDB 事务需要主动清理;
  • 浏览器可能冻结、回收或恢复后台页面;
  • BFCache 会保存页面快照,页面不一定立即销毁。

组件卸载时清理:

const controller = new AbortController()

window.addEventListener('resize', onResize, {
  signal: controller.signal
})

const worker = new Worker('/worker.js')

function dispose() {
  controller.abort()
  worker.terminate()
}

十一、把原文的 HTTP 八阶段更新为现代链路

原文将 HTTP 请求概括成:构建请求、查找缓存、准备 IP/端口、等待 TCP 队列、建立 TCP、发送请求、服务器处理、返回并断开。这个学习框架仍然有价值,但现代链路应补充:

  1. URL 解析和导航策略;
  2. HTTP 缓存、Service Worker 和 BFCache 判断;
  3. DNS、代理、连接复用和地址选择;
  4. HTTP/1.1、HTTP/2 或 HTTP/3 协议协商;
  5. HTTPS 的 TLS 和证书校验;
  6. 请求优先级、流复用和服务器处理;
  7. 重定向、响应头、压缩和缓存更新;
  8. 导航提交、HTML 流式解析和子资源并行加载;
  9. 渲染进程解析、样式、布局、绘制和合成。

“TCP 四次挥手后一定断开”也不是每个现代请求的固定结局:Keep-Alive、HTTP/2 多路复用和 HTTP/3 连接都可以复用,服务器和浏览器会依据协议与策略决定连接生命周期。

十二、面试题和正确回答

1. 为什么第二次访问页面可能更快?

可能原因包括:

  • HTML、JS、CSS、图片命中 HTTP 缓存;
  • DNS、连接、TLS 或预连接状态复用;
  • Service Worker 直接提供缓存;
  • 页面从 BFCache 恢复;
  • CDN 命中、服务器热缓存和数据恢复。

不能只回答“因为 Memory Cache”,也不能把后退恢复和普通重新导航混为一谈。

2. 打开 Chrome 一个 Tab 至少几个进程?

没有可靠的固定答案。浏览器可能已有 Browser、Viz/GPU、网络/服务进程,一个新页面还可能使用或创建渲染进程,扩展、视频解码和 WebView 又会改变数量。正确回答应是“由版本、平台、站点隔离、标签页关系、内存压力和启用功能决定”,而不是固定“四个”。

3. 为什么一个页面卡死有时会影响其他页面?

常见原因:

  • 页面与其他标签页共享渲染进程或相同站点资源;
  • Browser、Viz/GPU 或其他共享服务进程故障;
  • 系统内存压力导致整体换页或回收;
  • 扩展、驱动或浏览器自身崩溃;
  • 进程间通信或 GPU 资源出现问题。

但一个页面的普通无限循环通常主要阻塞自己的渲染主线程,不应说多进程“完全隔离一切影响”。

4. 同源和同站点有什么区别?

  • 同源:协议、主机、端口都相同,决定许多 DOM、Storage 和脚本访问边界;
  • 同站点(schemeful site):通常基于 scheme 和 registrable domain,用于 Cookie、站点隔离等更高层策略;
  • https://a.example.comhttps://b.example.com 可能同站点但不同源;
  • https://example.com:443https://example.com:8443 不是同源,但站点层面的规则不能直接替代源检查。

不要把“同顶级域和二级域”直接等同于同源。

十三、性能工具如何验证

浏览器原理不是背图,应该用工具观察:

Network

  • DNS、连接、TLS、TTFB、请求优先级;
  • HTTP 状态、缓存命中、AgeETag、压缩和 Server-Timing
  • HTML、CSS、字体和 LCP 图片的关键路径;
  • HTTP/2/3、协议、连接复用和请求瀑布。

Performance

  • 主线程任务、Long Task、输入延迟;
  • Style、Layout、Paint、Composite 时间;
  • 帧时间、掉帧、动画和强制同步布局;
  • JS 调用栈和第三方脚本。

Rendering/Layers

  • 图层数量和内存;
  • 合成原因;
  • Paint flashing、Layout Shift Regions;
  • GPU 栅格化和回退情况。

工具展示的是当前浏览器的实现证据,不应据此推导所有浏览器内部都完全相同。

十四、原文中需要保留但必须加限定的内容

  • 进程隔离提高稳定性和安全性,但不是所有 Tab 一进程;
  • 渲染主线程通常执行 JS、DOM、样式和布局,但合成、栅格化和 GPU 也会参与;
  • CSS/DOM/JavaScript 共同决定渲染结果,经典脚本可能阻塞解析;
  • DOM、计算样式、布局、绘制和合成是理解性能的有效模型;
  • transform/opacity 常常适合动画,但不保证一定绕过 Layout/Paint;
  • Memory/Disk Cache、连接数、进程数量和线程名称属于实现细节;
  • 同站点页面可能共享某些资源或进程,但不能因此跳过同源和权限检查;
  • GPU 可加速部分工作,也可能因为驱动、平台和内容回退到 CPU。

总结

  1. 浏览器是由多个进程、线程和服务协作的复杂宿主,具体结构随版本和平台变化;
  2. 多进程、沙箱和 Site Isolation 提供稳定性和安全隔离,但不是绝对隔离;
  3. 从 URL 到页面显示要经过缓存、DNS、连接、TLS、HTTP、导航提交、HTML 解析和渲染流水线;
  4. 不要把“每域 6 个连接”“每 Tab 一个进程”“GPU 直接绘制”“每任务后必渲染”等历史结论当成硬规则;
  5. 渲染可按 Style、Layout、Pre-paint、Paint、Layerize、Raster、Composite、Draw 理解;
  6. CSSOM、DOM、布局树和合成层是相关但不同的概念;
  7. 主线程长任务会影响输入和渲染,Worker、分块、rAF 和批量读写可以改善体验;
  8. 通过 Network、Performance、Rendering 和 Layers 面板验证真实瓶颈。

参考资料

457 DOCUMENTS · 10 COLLECTIONS
ARCHIVE SEARCH457 篇文章

SEARCH GUIDE

输入关键词开始搜索

支持搜索文章标题、所属分类和原始文档路径。

按分类浏览

10 COLLECTIONS