返回笔记

网站性能优化实战:从 8.88s 到 2s

性能优化webfontnginxgzipCDN

问题

一个 Vue3 + Vuetify + Vite 的个人站点,首次加载 8.88 秒,有时超过 11 秒。Chrome DevTools Performance 面板分析发现,耗时主要卡在 webfont 加载上——阻塞了 JS 执行长达 6 秒以上。

诊断

Vuetify 默认使用 Material Design Icons(@mdi/font),字体文件通过 CSS @font-face 声明加载。浏览器遇到外部字体资源时,默认行为是阻塞渲染直到字体下载完成。这不是 bug,是规范——但对于首屏体验来说,这个代价不可接受。

第一步:CDN 异步加载 webfont

将字体 CSS 从打包产物中剥离,改用 jsDelivr CDN 加载,并通过 rel="preload" + onload 切换的方式避免阻塞:

<link
  rel="preload"
  as="style"
  onload="this.rel='stylesheet'"
  href="https://cdn.jsdelivr.net/npm/@mdi/font@5.x/css/materialdesignicons.min.css"
/>

rel="preload" 让浏览器以高优先级下载资源但不阻塞渲染,下载完成后通过 onloadrel 切换为 stylesheet 应用样式。

首屏时间从 8.88s 降到 2s 以内。

第二步:nginx gzip 压缩

对文本类资源开启 gzip,减少传输体积:

gzip on;
gzip_min_length 1k;
gzip_buffers 4 16k;
gzip_http_version 1.1;
gzip_comp_level 4;
gzip_types text/plain text/css application/json application/javascript
           text/xml application/xml application/xml+rss text/javascript;
gzip_vary on;
gzip_proxied expired no-cache no-store private auth;
gzip_disable "MSIE [1-6]\.";

几个要点:

  • gzip_comp_level 4:压缩级别取中间值,平衡 CPU 开销和压缩率
  • gzip_types:显式指定需要压缩的 MIME 类型,避免漏压或误压
  • gzip_vary on:让代理服务器根据 Accept-Encoding 头正确缓存

第三步:静态资源缓存

对不常变的资源设置远未来过期时间,配合文件名哈希实现缓存更新:

# 图片、CSS、JS
location ~* \.(?:css(\.map)?|js(\.map)?|jpe?g|png|gif|ico|webp|svgz?)$ {
  expires 7d;
}

# 字体
location ~* \.(?:ttf|ttc|otf|eot|woff2?)$ {
  add_header Access-Control-Allow-Origin "*";
  expires 7d;
}

字体文件需要 CORS 头,否则跨域 CDN 场景下会加载失败。

结果

优化后首屏加载稳定在 2s 内,资源体积经 gzip 压缩后平均减少 60%+。核心结论:性能问题的根因很少在表面——先定位瓶颈,再选工具,顺序不能反。