浏览器【详解】页面加载过程(含页面加载时序图,页面加载性能优化方案)
浏览器页面加载时序图
触发浏览器加载新页面
以下情况都可能触发浏览器加载新页面
- 在浏览器地址栏输入 URL后按回车键
- 点击超链接
- 提交表单
- 路由管理器执行路由跳转
……
是否卸载当前页? Prompt for unload
用户触发页面跳转 / 关闭时,浏览器先询问是否 “卸载当前页”(比如表单未提交的提醒),对应 navigationStart 前的交互逻辑。
卸载旧页面 unload
执行旧页面的 unload 事件(比如销毁定时器、清理监听),标记 unloadStart(开始卸载)和 unloadEnd(卸载完成),为新页面加载腾资源。
URL 合法性校验
判断新的URL是否为有效 URL(如含.或协议头),若不是则默认调用搜索引擎(如输入 “天气” 会跳转至搜索结果页)。
URL 解析
浏览器的 UI 线程(负责处理用户交互的主线程)对 URL 进行解析,拆分出协议(如http/https)、域名(如example.com)、路径(如/index.html)、查询参数(如?id=1)等部分。
查询浏览器缓存
更详细的浏览器强缓存和协商缓存过程见 https://blog.csdn.net/weixin_41192489/article/details/136446539
Memory Cache(内存缓存)
存储内容:最近访问过的资源(如小型 JS、CSS、图片等),临时存储在内存中。
特点:读取速度极快(内存访问),但容量有限,且当浏览器关闭时会被清除。
Disk Cache(磁盘缓存)
存储内容:内存缓存放不下的大型资源(如较大的 JS 文件、CSS 文件、图片、视频等),或需要长期保存的资源(通过 HTTP 缓存策略标记)。
特点:容量较大,持久化存储(浏览器关闭后仍保留),但读取速度比内存缓存慢(磁盘 IO 操作)。
查询 DNS 缓存
- 浏览器本地存储的近期 DNS 记录(通常缓存 1 分钟~1 小时)
解析 DNS
将域名转化为 IP 地址
- 浏览器通过网络线程向本地 DNS 服务器(如运营商提供的114.114.114.114)发起查询。
- 本地 DNS 服务器若没有记录,会递归查询:根 DNS 服务器(指示顶级域名服务器,如.com)→ 顶级域名服务器(指示权威 DNS 服务器)→ 权威 DNS 服务器(返回目标 IP)。
建立网络连接
TCP 三次握手(HTTP 协议)
确保客户端与服务器之间的双向通信通道可靠建立
- 客户端发送SYN(同步)报文,请求建立连接。
- 服务器返回SYN+ACK(同步 + 确认)报文,同意连接。
- 客户端发送ACK(确认)报文,连接正式建立。
TLS 握手(HTTPS 协议)
在 TCP 基础上增加加密层,防止数据被窃听或篡改
- 客户端向服务器发送支持的加密套件列表。
- 服务器选择套件并返回数字证书(含公钥,由 CA 机构签发)。
- 客户端验证证书合法性,生成随机密钥并用服务器公钥加密后发送。
- 服务器用私钥解密得到密钥,双方后续通信使用该密钥对称加密。
客户端向服务器请求资源
- 请求行:方法(GET/POST等)、URL 路径、HTTP 版本(如HTTP/1.1)。
- 请求头:Host(目标域名)、User-Agent(浏览器信息)、Cookie(用户身份数据)等。
- 请求体:POST等方法携带的数据(如表单内容)。
客户端接收服务器响应
服务器处理请求后返回响应报文,包含:
- 状态码:表示请求结果(如200成功、404未找到、500服务器错误)。
- 响应头:Content-Type(资源类型,如text/html)、Cache-Control(缓存策略)等。
- 响应体:实际资源内容(如 HTML 文本、图片二进制数据)。
网络线程会根据响应头的Content-Type将资源分发到对应模块(如 HTML/CSS 交给渲染引擎,JS 交给 JS 引擎,图片交给图像解码器)
解析 HTML 构建 DOM 树
渲染引擎(如 Chrome 的 Blink、Firefox 的 Gecko)负责将 HTML 文本转化为浏览器可理解的 DOM 结构。
- 解析HTML:将 HTML 文本按标签规则拆分为一个个Token(如
<html>、<div>
、文本节点等),标记每个 Token 的类型(开始标签、结束标签、属性等)。 - 构建 DOM 树:根据 Token 的层级关系(如
<body>
包含<div>
),构建一个树形结构(Document Object Model),每个节点对应 HTML 中的一个元素或文本,包含其属性和父子关系。
JS 阻塞HTML 解析
默认情况下,当解析到<script>
标签时,HTML 解析会暂停(因为 JS 可能通过document.write()
修改 DOM,需等待 JS 执行完成)。
若<script>
添加了async或defer属性:
- async:JS 下载过程不阻塞 HTML 解析,下载完成后立即执行(可能打断解析)。
- defer:JS 下载过程不阻塞 HTML 解析,下载完成后等待 HTML 解析完毕再执行(按顺序执行多个defer脚本)。
优化策略:异步加载非关键 JS(async/defer)
解析 CSS 构建 CSSOM 树
CSS(层叠样式表)定义了元素的显示样式,渲染引擎需要将 CSS 规则转化为 CSSOM(CSS Object Model)。
-
解析 CSS:渲染引擎解析 CSS 文本(包括内联样式
<style>
、外链样式<link rel="stylesheet">
、元素属性style),提取样式规则(如div { color: red; }
)。 -
构建 CSSOM 树:CSSOM 是一个树形结构,每个节点对应 DOM 中的元素,存储该元素最终应用的样式(考虑样式继承、优先级:!important > 内联样式 > ID 选择器 > 类选择器等)。
CSSOM 的构建会阻塞渲染树生成(因为渲染需要 DOM+CSSOM 结合),但不阻塞 HTML 解析(HTML 解析和 CSS 解析可并行)。
生成渲染树(Render Tree)
渲染树是 DOM 树与 CSSOM 树的结合体,仅包含需要显示的节点,是后续布局和绘制的基础。
- 节点匹配:遍历 DOM 树,为每个可见节点(如display: none的节点会被过滤)匹配 CSSOM 中对应的样式规则。
- 样式计算:根据 CSS 优先级和继承规则,计算每个节点的最终样式(如一个p标签可能继承body的字体,同时被类选择器覆盖颜色)。
- 结构特点:渲染树的结构与 DOM 树不完全一致(如隐藏节点被移除),每个节点包含 “样式” 和 “布局信息”(后续步骤计算)。
重排 Reflow
计算渲染树中每个节点的几何位置和大小
- 从渲染树的根节点开始,递归计算每个节点的宽、高、相对父节点的坐标等。
- 考虑盒模型(margin、border、padding、content)、定位方式(static/relative/absolute/fixed)、浮动(float)等 CSS 规则。
触发场景:当 DOM 或 CSSOM 发生变化(如添加节点、修改宽高、窗口 resize)时,浏览器会重新计算布局
注意事项:性能消耗较高,应尽量避免频繁触发
重绘 Repaint
按渲染树的层级,从上到下绘制每个节点的可视部分(如文字、背景图、边框阴影),将节点转化为像素点(如颜色、背景、阴影、边框等)
优化机制:渲染引擎会将页面划分为多个图层(如视频、Canvas、定位元素可能单独成层),图层内的绘制互不影响,可减少重绘范围(如修改一个图层的内容,只需重绘该图层)。
合成 Composite
将所有图层的像素信息合并为最终的屏幕图像,显示在浏览器窗口中。
- 确定每个图层的绘制顺序(避免遮挡错误,如z-index高的图层在上)。
- 显卡的 GPU(图形处理器)负责图层合成,利用硬件加速提升效率。
合成操作不触发重排和重绘,性能消耗极低
优化策略:使用transform替代top/left
交互阶段:页面更新与事件响应
-
页面更新:若 JS 修改了 DOM 或样式,浏览器会重新执行 “布局→绘制→合成” 流程(或其中某一步),更新页面显示:
- 仅修改color等样式:触发重绘 + 合成。
- 修改width等几何属性:触发布局 + 重绘 + 合成。
- 修改transform:仅触发合成。
-
事件处理:用户操作触发事件(如click、scroll),UI 线程将事件传递给 JS 引擎,执行对应的事件处理函数(可能修改 DOM/CSS)。
页面加载性能优化方案
减少资源体积
资源精简
- 清理冗余代码(Tree Shaking):删除注释、调试代码(如console.log)、废弃功能的代码;避免引入未使用的库(如只用到 jQuery 的一个功能,却引入完整库)。通过 Webpack、Rollup 等工具,剔除 JS/CSS 中未被使用的代码(需配合 ES6 模块import/export)。
资源压缩
CSS/JS
使用csso(CSS)、terser(JS)等工具压缩,去除空格、注释、冗余代码;开启 Gzip/Brotli 压缩(需服务器配置,Brotli 压缩率高于 Gzip,节省 20%-30% 带宽)。
图片
- 选择合适格式:WebP/AVIF(比 JPEG/PNG 小 30%-50%),优先使用;对透明图片,WebP 比 PNG 更优。
- 使用 Squoosh(在线)、TinyPNG 批量压缩,保留视觉质量的同时减小体积
- 响应式图片:通过
<img srcset>
或<picture>
标签,根据设备分辨率加载不同尺寸图片(如移动端加载小图,PC 加载大图)。
字体
只引入必要的字重和字符集(如中文字体只保留常用字),使用font-display: swap避免文字闪烁。
延迟加载(Lazy Loading)
非首屏资源(如图片、视频、滚动后才显示的内容)设置loading="lazy"
(原生属性,兼容性良好),或通过 JS 监听滚动事件动态加载,减少初始加载压力
预加载(Preloading)
- 对关键资源(如首屏 CSS、核心 JS)使用
<link rel="preload" href="xxx" as="style">
,强制浏览器提前加载,避免因解析顺序导致的阻塞。 - 预连接(Preconnect):对跨域资源(如 CDN、第三方 API)使用
<link rel="preconnect" href="https://xxx.com">
,提前建立 TCP 连接,减少后续请求的握手耗时。 - 预获取(Prefetch):对可能被用户访问的资源(如跳转页面的 JS)使用
<link rel="prefetch" href="xxx">
,利用空闲时间加载,提升后续页面打开速度。
异步加载
广告、统计、社交插件等非关键 JS使用async(异步加载,加载完成后立即执行)或defer(异步加载,DOM 解析完成后执行)属性,避免阻塞 HTML 解析。
减少请求次数
资源合并
- 将多个小型 CSS/JS 文件合并为一个(如使用 Webpack 的splitChunks合并公共库),减少 HTTP 请求次数(每个请求都有 DNS 解析、TCP 握手等开销),避免过度合并导致单个文件过大(尤其移动端),可按页面或功能模块拆分。
浏览器缓存
- 强缓存:对不常变化的资源(如图片、库文件)设置Cache-Control: max-age=31536000(1 年),配合文件名哈希(如app.8f2b.js),确保资源更新时文件名变化,避免缓存失效问题。
- 协商缓存:对频繁变化的资源(如 HTML、API 数据)使用ETag或Last-Modified,让服务器判断是否返回 304(使用缓存)或 200(新资源),减少数据传输量。
- 缓存位置优化:内存缓存(短期)适合小资源,磁盘缓存(长期)适合大资源;通过Cache-Control: public允许中间代理(如 CDN)缓存,private则仅限浏览器缓存。
服务端缓存与 CDN
- CDN 缓存:将静态资源(CSS、JS、图片)部署到 CDN,利用 CDN 节点的地理位置优势,让用户从最近的节点加载资源(减少网络延迟);CDN 会自动缓存资源,配置合理的缓存规则(如与浏览器缓存策略匹配)。
- 服务器缓存:对动态内容(如 API 响应)使用 Redis、Memcached 等缓存数据,减少数据库查询耗时;对 HTML 页面,可使用服务端渲染(SSR)结合页面缓存,提升首屏加载速度。
优化网络连接(减少请求耗时)
DNS 优化
- DNS 缓存
- DNS 预解析:对页面中可能用到的域名(如 CDN 域名、第三方域名)添加
<link rel="dns-prefetch" href="https://xxx.com">
,提前解析 DNS(通常需要 20-100ms),避免在请求时阻塞。 - 减少域名数量:浏览器对同一域名的并发请求数有限制(如 Chrome 默认 6 个),但域名过多会增加 DNS 解析耗时,需平衡(如将资源分散到 2-3 个域名,既保证并发,又减少 DNS 开销)
启用 HTTP/2 或 HTTP/3:
- HTTP/2 支持多路复用(多个请求通过同一 TCP 连接并行传输),避免 HTTP/1.1 的 “队头阻塞” 问题;
- HTTP/3 基于 QUIC 协议(UDP),进一步优化连接建立速度和丢包恢复,适合弱网环境。
配置:通过 Nginx 或云服务开启 HTTP/2(需 SSL 证书),主流浏览器已广泛支持。
长连接
在 HTTP 响应头设置Connection: keep-alive(HTTP/1.1 默认开启),复用 TCP 连接,减少重复握手耗时。
压缩传输
- 除了资源本身的压缩(如 Gzip),对 API 接口返回的 JSON 数据,可使用gzip或br压缩(服务器配置),尤其对大数据量响应(如列表数据),压缩率可达 50% 以上。
- 对图片等二进制资源,使用 CDN 的 “智能压缩” 功能,自动根据设备和网络条件返回最优格式和质量。
加速解析与渲染
优化 HTML 解析
-
精简 HTML 结构:减少嵌套层级(如避免div嵌套过深),降低 DOM 树复杂度,加速解析。
-
关键 CSS 内联:将首屏渲染必需的 CSS(如头部、导航样式)内联
<style>
标签中,避免外部 CSS 文件加载延迟导致的 “无样式内容闪烁(FOUC)”。非首屏 CSS 可异步加载(如通过 JS 动态插入标签)。 -
非关键 JS 放在前,或使用async/defer
-
对必须在头部加载的 JS,可添加type=“module”(默认延迟执行,类似defer)
必须在头部加载 JS 的场景
- 需要在页面解析早期加载,但不希望阻塞 HTML 解析的模块脚本。
- 依赖其他模块的核心脚本(利用模块的依赖管理能力)。
<head><!-- 模块脚本:延迟执行,不阻塞 HTML 解析 --><script type="module">// 可使用 import 引入其他模块import { initApp } from './app.js';// 会在 HTML 解析完成后执行document.addEventListener('DOMContentLoaded', () => {initApp();});</script><!-- 引入外部模块 --><script type="module" src="./utils.js"></script> </head>
优化 CSS 解析,减少重排(Reflow)与重绘(Repaint)
https://blog.csdn.net/weixin_41192489/article/details/150582808