Skip to content

前端性能优化

优化指标

TTFB (Time To First Byte)

  • 定义:从发起 HTTP 请求到接收响应第一个字节的时间。
  • 意义:衡量服务器响应速度,影响后续渲染。
  • 测量:DevTools Network 面板或 performance.timing。
  • 目标:TTFB < 600ms。
  • 优化:
    • 使用 CDN 或边缘计算(如阿里云 OSS)。
    • 启用 HTTP/2 减少连接开销。

FCP (First Contentful Paint)

  • 定义:从页面开始加载到页面内容(文本、图片、Canvas 等)首次绘制的时间。
  • 意义:反映页面首次视觉反馈的速度,用户感知的加载开始点。
  • 测量:使用 Chrome DevTools 的 Performance 面板或 Lighthouse。
  • 目标:FCP < 1.8s(良好)。
  • 优化:
    • 减少服务器响应时间(TTFB):使用 CDN、后端缓存(如 Redis)。
    • 优化资源加载:内联关键 CSS、延迟非关键 JS(如 <script defer>)。
    • HTTP/2 推送:推送关键资源(如 app.js、main.css)。

LCP (Largest Contentful Paint)

  • 定义:从页面加载到最大内容元素(图片、文本块、视频等)绘制完成的时间。

  • 意义:反映主要内容加载速度,用户感知的核心体验。

  • 测量:Lighthouse 或 PerformanceObserver(Largest Contentful Paint 条目)。

  • 目标:LCP < 2.5s。

  • 优化:

    • 优化关键渲染路径:内联 CSS、预加载字体<link rel="preload">
    • 减少 JS 执行时间:优化微任务。
    • 服务端渲染(SSR)。

CLS (Cumulative Layout Shift)

累积布局偏移(CLS)是指页面元素在加载过程中视觉稳定性(加载时跳动)的指标。

  • 定义:页面加载期间视觉元素意外移动的累积分数。

  • 意义:反映页面稳定性和用户体验(避免点击错误)。

  • 测量:Lighthouse 或 LayoutShift 条目。

  • 目标:CLS < 0.1。

  • 优化:

    • 设置图片/广告尺寸:<img width="100" height="100">
    • 避免动态插入内容:使用 CSS min-height 占位。
    • 动画优化:用 transform 避免重排。

TBT (Total Blocking Time)

  • 定义:主线程被长任务(>50ms)阻塞的总时间,影响交互响应。

  • 意义:衡量页面交互卡顿程度。

  • 测量:Lighthouse 或 DevTools Performance。

  • 目标:TBT < 300ms。

  • 优化:

  • 拆分长任务:使用 requestIdleCallback 或 setTimeout。

  • 优化微任务:合并 Promise 链。

测量工具与方法

  • Chrome DevTools:

    • Performance 面板:分析 FCP、LCP、TBT,查看微任务(Promise、MutationObserver)和渲染时间。
    • Network 面板:检查 TTFB、资源加载时间。
  • Lighthouse:

    • 提供 FCP、LCP、TTI、TBT、CLS 和 SI 的综合报告。
    • 运行:DevTools Lighthouse 面板或 npm run lighthouse。
  • WebPageTest:

    • 分析多地域性能,提供 Speed Index 和详细时间轴。
  • Performance API:

网络优化

DNS 预解析

提前解析域名对应的 IP 地址,减少延迟。

  • dns-prefetch

提前进行 DNS 解析

  • preconnect

。通过抢先执行部分或全部握手(HTTP 为 DNS+TCP,HTTPS 为 DNS+TCP+TLS),预连接可加快未来从给定源加载的速度。

  • 实现示例:
html
<link rel="dns-prefetch" href="https://example.com">
<link rel="preconnect" href="https://example.com">
  • 注意事项:

如果一个页面需要与许多第三方域建立连接,将它们全部预连接可能会适得其反。 <link rel="preconnect"> 提示最好只用于最关键的连接。 对于其他连接,只需使用 <link rel="dns-prefetch">,以节省第一步 DNS 查询的时间。

资源预加载

提前加载页面需要的资源,避免后续请求。

  • preload(立即加载,马上要用)
html
<link rel="preload" href="/static/main.css" as="style">
<link rel="preload" href="/static/main.js" as="script">

as 属性告诉浏览器资源类型,方便正确应用缓存策略和优先级。

适合「当前页面」马上需要的关键资源。

  1. 首屏关键 CSS、JS
  2. Web 字体文件(防止字体闪烁)
  3. 首屏大图(如背景图)
  • prefetch(稍后加载,可能用得到)
html
<link rel="prefetch" href="/page2.js">

适合「下一页面」可能要用的资源(浏览器空闲时低优先级下载)。

prefetch 不会立即执行,而是缓存下来,下次用户进入对应页面时可直接使用。 非常适合 单页应用(SPA) 的路由级懒加载场景。

HTTP/2 优化

HTTP1主要存在以下问题:

  • 同一域名下连接数有限(一般最多 6 个)
  • 队头阻塞(前一个请求未返回,后续阻塞)
  • 无法多路复用(每个请求都要建立连接)

HTTP/2 优化

优化特性说明
多路复用 (Multiplexing)一个 TCP 连接可同时发送多个请求/响应,避免队头阻塞。
二进制分帧 (Binary Framing)把数据分成帧并标识来源,传输更高效。
头部压缩 (HPACK)减少 HTTP Header 体积。
服务器推送 (Server Push)服务器可提前推送客户端可能需要的资源。

CDN 加速

CDN(内容分发网络)是指将网站的静态资源(如图片、字体、CSS、JavaScript等)分布到全球多个节点,使用户从最近的节点获取资源,提高加载速度。

Gzip 与 Brotli 压缩

Gzip 与 Brotli 都是常用的压缩算法,用于减小文件大小,提高加载速度。

  • Gzip:由 GNU 项目开发,压缩率较高,支持所有浏览器。
  • Brotli:由 Google 开发,压缩率更高,支持现代浏览器(如 Chrome、Firefox)。

当浏览器发起 HTTP 请求时,会在请求头中带上:

http
Accept-Encoding: gzip, deflate, br

当浏览器发起 HTTP 请求时,会在请求头中带上:

http
Content-Encoding: br

Content-Encoding: gzip

静态压缩资源

一般是通过构建工具(如 Webpack、Rollup 等)在打包时自动压缩资源。

  • Webpack:使用 compression-webpack-plugin 插件压缩资源。
  • Rollup:使用 @rollup/plugin-terser 插件压缩资源。

动态压缩资源

nginx 也可以开启压缩功能,将压缩后的资源返回给客户端。

nginx配置

nginx

http {
    # ----------------------------
    # 启用 Gzip 实时压缩
    # ----------------------------
    gzip on;
    gzip_vary on;
    gzip_min_length 1k;
    gzip_comp_level 6;
    gzip_types
        text/plain
        text/css
        application/javascript
        application/json
        application/xml
        application/vnd.ms-fontobject
        application/x-font-ttf
        font/opentype
        image/svg+xml;
    gzip_proxied any;

    # ----------------------------
    # 启用 Brotli 实时压缩
    # (需安装 ngx_brotli 模块)
    # ----------------------------
    brotli on;
    brotli_comp_level 6;
    brotli_types
        text/plain
        text/css
        application/javascript
        application/json
        application/xml
        image/svg+xml
        font/woff2;

    # ----------------------------
    # 启用预压缩文件支持
    # ----------------------------
    gzip_static on;      # 优先使用 .gz 文件
    brotli_static on;    # 优先使用 .br 文件

    server {
        listen 80;
        server_name example.com;
        root /var/www/html;

        location / {
            # 让 Nginx 自动判断浏览器是否支持 br / gzip
            # 并优先返回对应的预压缩版本
            try_files $uri$br $uri.br $uri.gz $uri =404;

            # 若无预压缩文件,则实时压缩
            add_header Vary Accept-Encoding;
        }
    }
}

注意

  • 压缩等级 : 通常设置为 5~7;过高压缩等级影响构建速度
  • 压缩对象 : 仅压缩文本资源(HTML/CSS/JS/JSON),不压缩图片、视频等
  • 预压缩推荐 : 对静态资源(CDN、Nginx)使用预压缩,而非实时压缩
  • 同时提供两种格式 : .br 优先,.gz 兼容老浏览器
  • HTTP/2/3 兼容 : Brotli 在 HTTP/2、HTTP/3 中表现更优(头部压缩更好)
  • 验证压缩是否生效 : 使用浏览器 DevTools → Network → 查看 Content-Encoding

减少 HTTP1.1 请求次数

每个 HTTP 请求都包含以下开销:

    1. DNS 解析(域名 → IP)
    1. TCP 连接建立(三次握手)
    1. TLS 握手(HTTPS)
    1. 首包延迟(TTFB)
    1. 数据传输和响应解析

减少请求常用策略

  1. 静态资源合并(Bundle / Merge)

把多个文件合并成一个请求,适用于 JS、CSS、图标。

  • 打包工具(如 Vite、Webpack)自动合并依赖。
  • CSS @import 尽量减少嵌套。
  • JS 按模块打包或动态导入。
  1. 图片优化:使用合图与现代格式

(1)雪碧图(Sprite) 把多个小图标合并为一张大图,通过 CSS 定位显示。

css
.icon {
  background: url(sprite.png) no-repeat;
}
.icon-user { background-position: -10px -20px; }

(2)使用 Icon Font 或 SVG Sprite

  • Icon Font:用字体代替图片图标;
  • SVG Sprite:多个图标打包成一个 SVG 文件。
html
<svg><use href="#icon-search"></use></svg>

(3)使用 现代图片格式

WebP / AVIF 比 PNG、JPEG 小得多,可大幅减少传输体积。

  1. 资源内联(Inlining)

思路:把小型资源直接嵌入 HTML 或 CSS 文件中,省去单独请求。

(1)Base64 内联图片

css
.icon {
  background: url('data:image/png;base64,...');
}

base64 编码后的图片大小约为原始大小的 33%,因此在资源量不大的情况下,使用 base64 编码是一种不错的选择。

(2)内联 CSS / JS

html
<style>
  body { color: #333; }
</style>
<script>
  console.log('init');
</script>

建议:仅限于 小文件(<10KB),否则 HTML 会变大影响首屏。

  1. 使用 HTTP/2 或 HTTP/3 多路复用

HTTP/1.1 每次请求一个连接; HTTP/2/3 允许一个连接并发多个请求,极大降低连接开销。

在 HTTP/2 环境下:

  • 小文件拆分成本降低;
  • 但仍要减少不必要的请求(如重复依赖)。

为什么 HTTP/2 改善了请求问题

HTTP/1.1 的问题:

  • 一个 TCP 连接同一时刻只能处理一个请求;
  • 多个资源会触发队头阻塞(Head-of-line blocking);
  • 浏览器通常会为一个域名开多个连接(一般 6 个)。

HTTP/2 改进:

  • 多路复用(Multiplexing):一个 TCP 连接可以同时传输多个请求;
  • 头部压缩(HPACK):减少请求体积;
  • 服务器推送(Server Push):可提前推送资源;
  • 二进制分帧:更高效传输。

浏览器缓存

浏览器缓存是指浏览器在本地存储已下载的资源,当用户再次请求相同资源时,直接从缓存中获取,而不是从服务器重新下载。这可以减少网络请求次数,提高加载速度。

缓存策略

  • 强缓存:浏览器直接从缓存中获取资源,不向服务器请求。通过设置 Cache-ControlExpires 头来实现。
  • 协商缓存:浏览器向服务器请求资源,服务器根据资源的最后修改时间判断是否返回资源。通过设置 Last-ModifiedETag 头来实现。

Service Worker 缓存

Service Worker 是一种运行在浏览器后台的脚本,它可以拦截和处理网络请求,实现缓存、离线访问等功能。通过注册 Service Worker,可以对静态资源进行缓存,提高加载速度。

打包构建

类别优化策略
体积优化Tree Shaking、压缩、按需加载、图片优化
加载优化Code Splitting、Preload/Prefetch、HTTP/2
缓存优化hash 文件名、合理 Cache-Control
构建优化使用 Vite、并行压缩、缓存构建结果

优化技巧

总体优化方向

优化方向关键措施
DOM 操作减少操作次数,避免强制同步布局
事件处理节流/防抖,事件委托
动画渲染GPU 加速、使用 transform、will-change
数据逻辑缓存结果、延迟计算、使用 Worker
内存优化清理引用、解绑事件、虚拟列表
框架优化Vue 的 computed、React.memo 等

DOM 与渲染优化

优化项检查要点示例
减少 DOM 操作次数批量修改后一次插入使用 DocumentFragment 合并插入节点
避免频繁读取布局信息先读后写,避免强制重排不要在修改样式后立即读取 offsetWidthgetBoundingClientRect()
样式批量修改一次性更改 class 或使用 CSS 变量element.classList.add('active', 'show')
避免重绘(Repaint)控制视觉变化属性优先使用 transformopacity 代替 topleft
图片尺寸固定避免布局抖动<img width height> 提前定义大小

事件与交互优化

优化项检查要点示例
节流与防抖高频事件使用 throttle/debounce滚动、输入、窗口 resize
事件委托列表、表格、动态元素用事件委托父元素代理子元素点击
避免阻塞事件循环不在事件中执行重计算将耗时任务丢给 Web Worker

动画与过渡优化

优化项检查要点示例
使用 CSS 动画优先使用硬件加速属性transformopacity
启用 GPU 加速使用合成层transform: translateZ(0)will-change
控制动画复杂度不要同时改变 layout 属性避免频繁操作 width/height/top/left
使用 requestAnimationFrameJS 动画同步浏览器帧率替代 setInterval

脚本执行优化

优化项检查要点示例
异步加载非核心脚本使用 asyncdefer<script defer src="...">
延迟执行任务使用 requestIdleCallback在空闲时处理非关键逻辑
缓存计算结果重复计算结果记忆化memo[key] = expensiveCalc()
减少对象深层拷贝控制 JSON.parse/clone使用结构共享或浅拷贝

数据与内存优化

优化项检查要点示例
虚拟列表渲染大数据列表只渲染可视区域使用 Vue Virtual Scroll / react-window
避免内存泄漏清除定时器、解绑事件clearInterval()removeEventListener()
减少无效缓存控制缓存大小限制 localStorage/sessionStorage 使用量
避免重复数据计算缓存接口结果useMemocomputed、或本地 Map 缓存

框架级优化

优化项VueReact
渲染控制v-show 代替频繁切换的 v-if拆分组件、减少 diff 范围
计算缓存computedwatch 合理使用useMemouseCallback
避免不必要响应式不将静态数据放入响应式系统使用普通变量存放固定数据
懒加载与异步组件defineAsyncComponent()React.lazy() + Suspense

图像与多媒体优化

优化项检查要点示例
使用合适格式WebP、AVIF 优先<img src="a.webp" />
按需加载懒加载图片<img loading="lazy">
压缩图片TinyPNG / imagemin构建时自动压缩
控制视频体积分辨率与码率合理video 加上 preload 控制

调度与任务管理

优化项检查要点示例
任务分片拆分大计算任务分块执行,避免主线程卡顿
使用 Worker密集计算放入 Worker避免阻塞 UI 渲染
优先级控制requestIdleCallback / RAF非关键逻辑延迟执行

重点原则总结

  • 能不操作 DOM 就不要频繁操作
  • 能异步就不要同步阻塞主线程
  • 能缓存的结果就不要重复计算
  • 能复用的组件就不要频繁销毁重建
  • 能交给浏览器的(CSS动画/GPU加速)不要手写 JS 实现