现代浏览器架构
在互联网高速发展的今天,浏览器已成为用户与网络世界连接的核心工具。从简单的文档查看器演变而来,现代浏览器不仅要快速解析网页,还需同时支持复杂的多媒体内容、交互式应用以及高性能的后台任务。为了满足这些需求,浏览器的架构变得愈发复杂且精妙——它们采用模块化、多进程和安全优先的设计,以提升性能、稳定性和安全性
本文将以“现代浏览器架构 - Chrome”为核心,探讨其背后的设计理念、工作流程以及各关键组件的职责,揭示这一网络时代基础设施如何高效运作、应对复杂挑战,并为开发者与用户提供卓越的体验
CPU和GPU
CPU(Central Processing Unit,中央处理器)
是计算机的大脑,负责执行所有的计算任务和控制操作。它是计算机中最核心的部分,决定了系统的运算速度和能力
CPU 核心(如图中描述的办公室员工)可以处理许多不同的任务 逐一查看这些状态信息它能处理数学、美术等各种任务,而且知道如何回复 。过去,大多数 CPU 都是单芯片。核心就像是生活在 同一条状标签。在现代硬件中,您通常会获得多个核心,从而提供更好的计算能力 添加到手机和笔记本电脑上
GPU(Graphics Processing Unit,图形处理器)
是专门用于处理图形和并行计算的硬件单元。最初,GPU 主要用来加速图形渲染,但如今已广泛用于机器学习、科学计算等需要并行处理的场景。
图像处理与 CPU 不同 GPU 擅长处理简单的任务,但可以同时跨多个核心。它最初是为了处理图形而开发的。这就是为什么 "使用 GPU"或“支持 GPU”与快速渲染和流畅交互有关。 近年来,随着 GPU 加速计算的推出,越来越多的计算可以在 仅使用 GPU
CPU 和 GPU 是计算机中两种核心处理单元,各自擅长不同类型的任务。CPU 提供强大的通用计算能力,适合执行单一线程为主的任务;而 GPU 的多核心架构则使其在需要大量并行计算的任务中表现卓越。这两者相辅相成,共同构成现代计算设备的强大性能基础
进程和线程
进程(Process)
是操作系统中独立运行的程序实例。它是一个具有独立资源和内存空间的执行单元,代表了一个正在运行的应用或任务
线程(Thread)
是进程中的最小执行单元。一个进程可以包含一个或多个线程,这些线程共享进程的内存空间和资源
启动应用时,系统会创建一个进程。程序可能会创建线程来帮助它 可以运行,但这是可选操作。操作系统为这一过程提供了一个“平台”运行 并且所有应用状态都保存在该私有内存空间中关闭 该进程也会停止,操作系统也会释放内存
进程可要求操作系统启动另一个进程以运行不同的任务。当 内存的不同部分会分配给新进程如果两个进程需要 谈话时,他们可以使用私密通信 (IPC) 来实现。许多应用 按照这种方式工作,如果工作器进程无响应,可以将其重启 而无需停止运行应用不同部分的其他进程
浏览器架构
Chrome 浏览器采用了多进程、多线程
架构,这种设计旨在实现更高的性能、稳定性和安全性。通过进程与线程的合理划分,Chrome 能够高效地处理用户操作、页面渲染和后台任务
Chrome 的架构既包含多个独立的进程,也在每个进程内运行多个线程。主要组件和职责如下:
多进程架构
浏览器进程(Browser Process)
:- 唯一的主进程,负责浏览器的整体协调
- 主要功能:
- 管理用户界面(地址栏、书签栏、标签管理等)
- 网络请求(发起 HTTP 请求并接收响应)
- 管理文件和存储
- 控制和分配其他进程
渲染进程(Renderer Process)
:- 多实例:每个标签页通常对应一个渲染进程(也可能多个页面共享同一进程,取决于浏览器的优化策略,如:页面中点击打开同域新页面会共享一个渲染进程)
- 主要功能:
- 执行 HTML、CSS、JavaScript
- 构建 DOM 树、CSSOM 树并渲染页面
- 沙盒隔离,限制操作系统的直接访问
GPU 进程(GPU Process)
:- 全局唯一,负责硬件加速任务
- 提供 2D/3D 图形渲染的支持,如 WebGL、Canvas 和视频解码
插件进程(Plugin Process)
:- 独立于渲染进程,用于运行浏览器插件(如 Flash)
- 每个插件运行在自己的进程中,避免插件崩溃影响浏览器
扩展进程(Extension Process)
:每个扩展运行在独立的进程中,提高浏览器的稳定性
多线程架构
每个进程内部进一步包含多个线程,主要线程及其职责如下:
浏览器进程的线程
主线程
:负责用户界面和其他核心逻辑网络线程
:处理所有网络相关任务(如 HTTP 请求和响应)存储线程
:负责管理本地存储、缓存和数据库(如 IndexedDB)文件线程
:处理文件读写任务
渲染进程的线程
主线程
:处理 DOM 操作、页面布局和 JavaScript 执行合成线程(Compositor Thread)
:- 负责页面渲染的合成部分,将主线程生成的图层合成为最终显示的页面
- 在主线程繁忙时,能保持页面的滚动和动画流畅
光栅化线程(Rasterization Thread)
:使用 GPU 加速绘制页面内容,将图层绘制为位图工作线程(Worker Threads
):执行 Web Worker 和 Service Worker 等后台任务
为什么要多进程
Chrome 浏览器采用了多进程架构,将浏览器的不同功能模块(如 UI、渲染、插件、扩展等)分配到独立的进程中运行。相比传统的单进程架构,这种设计具有以下显著优势
稳定性
在一个标签页加载一个占用大量资源的网页(如复杂的动画或错误的脚本),即使其崩溃,也不会中断其他标签页的正常使用
如果某个标签页无响应,您可以关闭无响应的标签页并继续操作,同时保持 其他标签页处于活动状态如果所有标签页都在一个进程中运行,那么当一个标签页无响应时,所有标签页 标签页无响应太可惜了
安全性 - 网站隔离
访问含有恶意脚本的页面时,由于渲染进程被隔离,该脚本无法访问用户的文件或其他页面的数据
渲染进程运行在沙盒中,被严格限制对文件系统、设备和操作系统资源的访问
网站隔离 是最近 在 Chrome 中引入了一项功能,可为每个跨网站 iframe 运行单独的渲染程序进程
我们一直在介绍每个标签页模型一个渲染程序进程,该模式允许跨网站 在单个渲染器进程中运行的 iframe,不同网站之间共享内存空间。 在同一个渲染器进程中运行 a.com 和 b.com 似乎没有问题。 同源政策 是网络的核心安全模型;可确保一个网站无法访问来自其他网站的数据 未经同意。绕过此政策是安全攻击的主要目标。 进程隔离是分隔网站最有效的方法
从 Chrome 67 开始,在桌面设备上默认启用网站隔离功能后,标签页中的每个跨网站 iframe 会获得一个单独的渲染程序进程
高效利用内存
浏览器进程也采用同样的方法。Chrome 正在进行架构更改 将浏览器程序的各部分作为服务运行,允许拆分为不同的进程 也可以将其合并为一个应用
一般来说,当 Chrome 在功能强大的硬件上运行时,可能会将每项服务拆分为 不同的进程赋予更高的稳定性,但如果在资源受限的设备上,则 Chrome 将服务整合到一个进程中,从而节省内存。类似的整合方法 在此次变更之前,Android 等平台上都使用了内存使用量较少的进程
浏览器内核
浏览器内核是浏览器的核心组件,负责浏览器与操作系统的交互、网页资源的加载、以及管理和调度其他内核模块(如渲染引擎、JavaScript 引擎、网络模块等)。它是浏览器的基础架构,保证了浏览器能够处理网页的渲染、交互和其他功能
不同的浏览器内核实现方式不同,常见的浏览器内核有:
Chromium 内核(Blink)
:这是 Google Chrome、Microsoft Edge、Opera 等浏览器的基础内核。Chromium 是一个开源项目,而 Blink 是 Chromium 项目中的渲染引擎Gecko 内核
:Mozilla Firefox 浏览器使用的内核。Gecko 渲染引擎负责网页的渲染WebKit 内核
:Safari 浏览器使用的内核。WebKit 最初由 Apple 开发,它是一个开源项目,曾经是 Chrome 的渲染引擎(但后期 Chrome 切换到 Blink)EdgeHTML 内核
:Microsoft Edge(在其初始版本中)使用的内核,是一个由 Microsoft 开发的渲染引擎,后来 Edge 切换到 Chromium 内核(即 Blink)
浏览器引擎:
- Chrome:v8
- Firefox:SpiderMonkey
- Safari:JavascriptCore(Nitro)
- Edge:以前基于Chakra,后来也是v8
浏览器URL回车后发生了什么
浏览器URL回车后发生了什么
❓这是一个老生常谈、面试高频的一个问题‼️
整个过程涉及浏览器多个进程的协调工作。每个进程负责不同的任务,确保网页能够被正确加载、渲染和展示给用户
用户输入 URL:当你在地址栏输入 URL 后,浏览器开始进行一系列的操作来请求并显示网页
主进程(Browser Process):
- 解析 URL:浏览器的主进程负责解析用户输入的 URL,确定协议、域名、路径等信息
- 创建新的标签页进程:如果没有现有标签页在执行此请求,浏览器的主进程会创建一个新的渲染进程来处理该页面的渲染
DNS 解析:浏览器需要将 URL 中的域名解析为 IP 地址,以便连接到服务器。这个过程是通过 DNS(域名系统)来完成的
主进程(Browser Process)和网络进程(Network Process):
- DNS 查询:主进程会向网络进程发起 DNS 查询请求,查询域名对应的 IP 地址
- 查询缓存:主进程首先会检查浏览器的DNS缓存,看是否有该域名的IP地址。如果缓存中存在,就直接使用,否则发起 DNS 请求
- DNS 响应:网络进程向主进程返回解析后的 IP 地址
TCP 连接:一旦得到服务器的 IP 地址,浏览器需要与服务器建立 TCP 连接(对于 HTTPS,还会进行 TLS 握手)。
网络进程(Network Process):
- 建立连接:网络进程通过套接字(Socket)与目标 IP 地址建立 TCP 连接
- TLS 握手(如果是 HTTPS):如果 URL 使用的是 HTTPS 协议,网络进程会与服务器通过 TLS 进行加密握手,确保数据传输的安全性
发送 HTTP 请求:连接建立后,浏览器将向服务器发送 HTTP 请求,获取网页内容
主进程(Browser Process)和网络进程(Network Process):
- 构建 HTTP 请求:主进程生成 HTTP 请求消息(如 GET 请求),包括请求头、Cookies、缓存控制等信息
- 发送请求:网络进程将 HTTP 请求发送到目标服务器
服务器响应:服务器收到请求后,会处理并返回响应内容,浏览器开始接收响应数据
网络进程(Network Process):
- 接收响应:网络进程接收服务器返回的 HTTP 响应,通常包括状态码、响应头、响应体(网页内容)
- 缓存和存储:网络进程会根据响应头的缓存控制策略,将数据存储到缓存中,以便下次请求时可以更快地响应
页面解析、绘制合成
文档加载完后会通知浏览器进程,tab的加载按钮就停止了;文档卸载时浏览器进程和渲染进程要进行通信确保unload事件
页面渲染过程
生成DOM树
:浏览器从上到下逐行解析 HTML 文本,将 HTML 标签转换成 DOM 树的节点。每个 HTML 标签都对应一个 DOM 元素;如果浏览器在解析过程中如果遇到style
标签时会开辟新线程解析它;若遇到script
标签,根据3种情况执行行内script
:暂停解析DOM并立即执行当前script<script defer>
:主线程继续解析DOM,此时开辟一个新线程来加载js脚本,当文档解析完毕后,立即执行当前js脚本(脚本执行时机早于ContentLoaded
事件)<script async>
:主线程继续解析DOM,此时开辟一个新线程来加载js脚本,当文档解析完毕触发ContentLoaded
事件后执行此脚本
生成CSS树
:当浏览器遇到外部<link>
标签或内部<style>
标签时,会加载并解析 CSS 样式。CSS 样式会被转换为 CSSOM 树生成渲染树
:渲染树是从 DOM 树和 CSSOM 树中筛选出可见元素并计算其样式后构建的。只有在页面中可见的元素才会出现在渲染树中。例如:display: none
的元素不会出现在渲染树中以及它们的样式信息生成布局树(Layout)
:布局阶段是计算页面中每个元素的确切位置和尺寸。根据渲染树中每个节点的样式(如 width、height、margin、padding、position 等),浏览器会计算每个元素的精确位置和大小。这个过程称为Reflow
绘制(Paint)
:绘制过程是将布局阶段计算出的几何信息转换为实际的像素。每个元素会根据其样式(如背景色、边框、文本、阴影等)被绘制到屏幕上;一些元素可能被绘制到不同的图层上(例如,浮动元素、动画元素、固定定位的元素等)。这些层会独立地进行绘制和合成合成(Compositin)
:合成阶段是指将各个图层合并为一个最终图像,并将其显示在屏幕上;当页面包含多个层时,浏览器会将这些层单独处理,并最终合成一个完整的图像。这个过程可能通过 GPU 加速进行,以提高性能分层
:为了确定哪些元素需要位于哪些层,主线程会遍历布局树来创建层树(此部分在开发者工具性能面板中称为“Update Layer Tree”)。如果网页中本应属于单独图层的某些部分(例如滑入式侧边菜单)没有出现,您可以使用 CSS 中的 will-change 属性来提示浏览器光栅化位图
:创建层树并确定绘制顺序后,主线程会将该信息提交到合成器线程。然后,合成器线程会光栅化每个图层。图层的大小可能相当于页面的全部长度,因此合成器线程会将它们分成多块图块,并将每个图块发送到光栅线程。光栅线程会光栅化每个图块并将它们存储在 GPU 内存中
图块光栅化后,合成器线程会收集图块信息(称为“绘制四边形”)来创建合成器框架
然后,通过 IPC 将合成器帧提交到浏览器进程。此时,可以从界面线程(针对浏览器界面更改)或针对扩展程序的其他渲染程序进程添加另一个合成器帧。系统会将这些合成器帧发送到 GPU,以便在屏幕上显示。如果发生滚动事件,合成器线程会再创建一个合成器帧以发送到 GPU
合成的好处是,在不涉及主线程的情况下完成。合成器线程不需要等待样式计算或 JavaScript 执行。因此,仅合成动画被认为是实现流畅性能的最佳选择。如果需要再次计算布局或绘制,则必须涉及主线程
渲染优化
从上文我们知道页面渲染是一个非常昂贵的过程,应尽量避免页面的重新渲染
避免频繁的重排和重绘
重排(Reflow)
:修改布局相关的属性或样式(如 width、height、margin) 重绘(Repaint)
:修改颜色、背景、阴影等不影响布局的样式
例如批量修改 DOM 和样式:将多次 DOM 操作合并为一次性操作、使用 DocumentFragment 进行操作:
// 避免多次操作 DOM
const el = document.getElementById('example');
el.style.width = '100px';
el.style.height = '100px';
// 合并修改
el.style.cssText = 'width: 100px; height: 100px;';
// createDocumentFragment
const fragment = document.createDocumentFragment();
for (let i = 0; i < 100; i++) {
const newNode = document.createElement('div');
newNode.textContent = `Item ${i}`;
fragment.appendChild(newNode);
}
document.body.appendChild(fragment);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
分层优化
减少绘制次数并提升合成效率,明确指定需要独立图层的元素,利用 GPU 加速渲染动画和滚动
will-change: transform, opacity;
合成优化
充分利用 GPU 加速减少 CPU 开销
优化动画的样式,只使用 合成属性:
transform
、opacity
、will-change(最后使用)
;避免触发布局或绘制的动画,例如 top、left、width 和 height 的动画为滚动区域添加 will-change: transform; 或 position: fixed;,使浏览器利用 GPU 优化滚动渲染
参考文献
- developer browser part1
- developer browser part2
- developer browser part3
- developer browser part4
- how browsers work