本文目录导读:
这是一个非常核心的前端性能优化问题,静态资源(JS、CSS、图片、字体、视频等)的加载速度直接影响页面的首屏渲染时间和交互响应。
以下是经过行业验证的七大类静态资源优化方法,从原理到具体实践:
体积压缩:减少网络传输字节
这是最直接、性价比最高的手段。
- 代码混淆与压缩:
- JS:使用 Terser(Webpack自带)、UglifyJS,删除注释、缩短变量名、去除空格。
- CSS:使用 clean-css、cssnano,合并相同的选择器、压缩颜色值。
- HTML:使用 html-minifier,去除空格、注释。
- 启用文本压缩:
- Gzip / Brotli(推荐):服务器配置,对于纯文本文件(JS、CSS、HTML、JSON),Brotli 比 Gzip 压缩率高 20%-30%。
- 检查:在浏览器开发者工具
NetworkTab 中,查看Response Headers是否有Content-Encoding: br。
- 图片压缩:
- 有损压缩:TinyPNG、imagemagick,降低颜色质量,肉眼难以察觉。
- 无损压缩:OptiPNG、svgo(针对 SVG)。
- 移除无用代码:
- Tree Shaking:打包工具自动移除“死代码”(未引用的函数、模块)。
- 移除冗余的 polyfill:使用
@babel/preset-env配合browserslist,只给低版本浏览器转译语法。 - 移除未使用的 CSS:使用 PurgeCSS(配合 Tailwind CSS 或原生项目),剔除未使用的样式类。
缓存策略:避免重复下载
充分利用浏览器缓存,减少网络请求。
- 强缓存:
- Cache-Control:
max-age=31536000:对于版本号固定的资源(如main.abc123.js),设置一年缓存。immutable:告诉浏览器无需询问服务器,直接用缓存。
- Expires:HTTP/1.0 的字段,现在通常被 Cache-Control 替代。
- Cache-Control:
- 协商缓存:
- Last-Modified / ETag:对于 HTML 文件或需要实时更新的资源,让浏览器每次请求时向服务器验证文件是否修改(返回 304 状态码则使用缓存)。
- 文件指纹(Hash)
- 内容哈希:在文件名中加入哈希值(如
style.12345.css),只有文件内容变化时哈希才变。 - 策略:对
vendor(第三方库)和app(业务代码)分开打包。vendor变化频率极低,可以设置极长的max-age。
- 内容哈希:在文件名中加入哈希值(如
加载策略:控制资源加载顺序和时机
不要一股脑加载所有资源。
- 异步加载 JavaScript:
<script defer>:等 HTML 解析完再执行,保留顺序。<script async>:下载完就执行,不阻塞解析,但会打乱顺序(适用于独立脚本,如广告、统计)。- 动态导入:
import(‘./heavyModule.js’).then(...):按需加载,只在用户点击按钮或跳转路由时加载。
- 非关键 CSS 异步加载:
- Critical CSS(关键 CSS):将首屏用到的 CSS 内联到
<head>中,非关键的 CSS 用media="print" onload="this.media='all'"或rel="preload"延迟加载。
- Critical CSS(关键 CSS):将首屏用到的 CSS 内联到
- 图片懒加载:
- Native Lazy Loading:
<img loading="lazy">(浏览器原生支持,最简单)。 - Intersection Observer API:监听图片是否进入视口,进入后再设置
src属性。
- Native Lazy Loading:
资源重用与传输优化
- CDN 加速:
- 将静态资源部署到全球边缘节点,让用户从最近的服务器下载。
- 注意:使用 CDN 时,务必设置 CORS(跨域)头,因为 CDN 域名通常与主站不同。
- HTTP/2 多路复用:
- 避免:HTTP/1.1 时代的资源合并(如 CSS Sprites、JS 合并成一个大包)。
- 建议:拆分小文件,因为 HTTP/2 可以在一个 TCP 连接上并行传输多个文件,且互不阻塞。
- 使用 Preload / Prefetch / Preconnect:
- Preload:
<link rel="preload" href="critical.js">:强制浏览器立即加载当前页面必须的资源。 - Prefetch:
<link rel="prefetch" href="next-page.js">:预测用户下一步会访问的页面,空闲时加载。 - Preconnect:
<link rel="preconnect" href="https://api.com">:提前与第三方服务器建立连接(DNS + TCP + TLS握手)。
- Preload:
图片特定优化
图片通常占据页面 60% 以上的体积,需要单独处理。
- 使用现代格式:
- WebP:比 JPEG 小 25%-35%,支持有损、无损和透明度。
- AVIF:比 WebP 压缩率更高(约 50%),但兼容性稍差。
- 方案:使用
<picture>标签提供 fallback:<picture> <source srcset="img.avif" type="image/avif"> <source srcset="img.webp" type="image/webp"> <img src="img.jpg" alt="图片描述"> </picture>
- 响应式图片:
srcset和sizes:为不同屏幕宽度提供不同分辨率的图片(如 320w, 640w, 1280w)。
- CSS 渐变代替图片:纯色或简单渐变用 CSS 实现,避免 HTTP 请求。
- 图标字体或 SVG Sprite:代替多张小图标 PNG,体积小且矢量不失真。
字体优化
- WOFF2 格式:比 WOFF 压缩率高出 30%-40%。
- 子集化字体:如果只用到几个中文汉字(如 Logo),用 FontSquirrel 或
glyphhanger生成只包含这些字体的文件。 - 使用
font-display: swap:先使用系统字体占位,字体加载完成后再替换,避免不可见文字(FOIT)问题。
监控与自动化
- 性能预算:设定 JS 主包不超过 200KB,图片总大小不超过 1MB,在 CI/CD 流水线中监控。
- Lighthouse 审计:定期运行,关注“Opportunities”和“Diagnostics”部分。
- Web Vitals:监控 LCP绘制,要求 < 2.5s)、FID/INP(交互延迟)、CLS(布局偏移)。
最佳实践路线图
如果你是页面负责人,建议按照这个优先级逐步实施:
- 基础层:启用 Brotli 压缩 + 配置强缓存 + 部署 CDN,这些不需要改代码,见效最快。
- 构建层:Tree Shaking + 代码压缩 + JS/Webpack 分包(将
react、vue等第三方库单独打包)。 - 图片层:使用 WebP + 懒加载(
loading=lazy),图片通常是优化空间最大的地方。 - 加载层:异步加载非核心 JS + 提取关键 CSS。
一个常见的误区:盲目合并所有 JS 成一个文件,在 HTTP/2 时代,这反而可能导致首屏阻塞变得严重,正确的做法是按需加载、分块打包。
标签: 文件压缩