人人都会AI编程

22.2 静态资源优化:图片压缩、字体优化、雪碧图

更新时间:2026-07-11

静态资源往往是页面体积的“大头”——一张未压缩的高清图片可能比整个应用的 JS 包还大,一个包含全量字符的字体文件动辄几 MB。对这些资源的优化,直接关系到首屏加载速度、用户流量消耗和页面的交互体验。


图片压缩

1. 构建时自动压缩

不要让设计师或开发者在本地手动处理每一张图片,把压缩交给构建工具。使用 Vite 时,可以引入 vite-plugin-imagemin

npm install vite-plugin-imagemin -D
// vite.config.js
import viteImagemin from 'vite-plugin-imagemin'

export default {
  plugins: [
    viteImagemin({
      gifsicle: { optimizationLevel: 7 },
      optipng: { optimizationLevel: 7 },
      mozjpeg: { quality: 80 },
      pngquant: { quality: [0.65, 0.8] },
      svgo: {
        plugins: [{ name: 'removeViewBox', active: false }]
      }
    })
  ]
}

这个插件会在生产构建时自动压缩 assets 目录下的图片。压缩效果明显:一张 1.2MB 的 PNG 往往能缩减到 300KB 左右,肉眼几乎看不出差异。

2. 选择现代格式

WebP 和 AVIF 格式在相同质量下体积小得多。可以通过 <picture> 标签提供多格式备选,让浏览器自动选择支持的最优格式:

<picture>
  <source srcset="/images/banner.avif" type="image/avif" />
  <source srcset="/images/banner.webp" type="image/webp" />
  <img src="/images/banner.jpg" alt="banner" loading="lazy" />
</picture>

如果项目中图片较多,推荐使用 vite-plugin-imagemin 同时生成 WebP/AVIF 版本,或者用 CDN 服务(如七牛、阿里云 OSS)的“图片处理”参数实时转换格式并裁剪尺寸。

3. 响应式图片 + 懒加载

不同屏幕尺寸不需要加载同一张大图。结合 srcsetsizes 属性,浏览器会根据设备像素比选择合适的图片:

<img
  src="/images/product-800.jpg"
  srcset="
    /images/product-400.jpg 400w,
    /images/product-800.jpg 800w,
    /images/product-1200.jpg 1200w
  "
  sizes="(max-width: 600px) 100vw, 50vw"
  loading="lazy"
  alt="产品图"
/>

loading="lazy" 是原生懒加载属性,无需引入第三方库,在 Vue 项目中可以直接在模板中使用。

4. 组件化处理图片

封装一个 <OptimizedImage> 组件,统一处理 WebP 回退、懒加载和占位图,避免每次手动重复写一堆属性:

<template>
  <picture>
    <source v-if="webp" :srcset="webp" type="image/webp" />
    <img :src="src" :alt="alt" loading="lazy" @error="onError" />
  </picture>
</template>

字体优化

1. 优先使用系统字体

如果设计允许,尽量减少自定义字体文件。一套系统字体栈可以完全避免网络请求:

body {
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
}

2. 字体子集化

中文或日文字体文件通常很大,但实际用到的字符可能只有几百个。使用工具删除未使用的字形,能大幅瘦身。

  • 字体压缩工具:fonttools(命令行)、fontmin(Web 工具)
  • 在构建时集成:可以在 vite.config.js 中编写一个简单插件,在打包前根据页面文本自动生成子集字体文件。

如果项目中只有固定的字符(如公司名称、Logo 文字),手动提取出这些字符生成字体文件,体积可缩小几十倍。

3. 使用 WOFF2 格式

现代浏览器普遍支持 WOFF2,它比 TTF 或 WOFF 体积更小。在 CSS 中声明字体时,将 WOFF2 放在前面,浏览器会优先加载:

@font-face {
  font-family: 'MyFont';
  src: url('/fonts/MyFont.woff2') format('woff2'),
       url('/fonts/MyFont.woff') format('woff');
  font-display: swap; /* 关键属性 */
}

4. font-display 控制加载行为

font-display: swap 允许浏览器先用系统字体渲染,待自定义字体加载完成后再替换。避免了 FOIT(不可见文本闪烁)导致的白屏等待。在 Vue 项目的全局样式中务必加上这个属性。

5. 预加载关键字体

如果首屏有一个作为标题的自定义字体,可以提前声明 preload,让浏览器更早发现字体文件:

<link rel="preload" href="/fonts/heading.woff2" as="font" type="font/woff2" crossorigin />

注意 crossorigin 属性是必需的,即使字体来自同源。


雪碧图

雪碧图(CSS Sprite)将多个小图标合并到一张大图上,通过 background-position 定位显示所需部分。在 HTTP/1.1 时代,这能大幅减少请求数;如今 HTTP/2 支持多路复用,请求数的影响减弱,但雪碧图依然能提升图标加载的稳定性,减少由于某个图标加载失败导致的视觉空白。

1. 使用 SVG 雪碧图(更推荐)

现代项目中,SVG 图标更灵活,且可以直接内联。将多个 SVG symbols 放在页面开头,通过 <use> 引用即可:

<svg style="display:none">
  <symbol id="icon-search" viewBox="0 0 24 24">
    <path d="M15.5 14h-.79l-.28-.27..."/>
  </symbol>
  <symbol id="icon-home" viewBox="0 0 24 24">
    <path d="M10 20v-6h4v6..."/>
  </symbol>
</svg>

使用时:

<svg class="icon"><use href="#icon-search" /></svg>

在 Vite 中,可以用 vite-plugin-svg-icons 自动收集 src/icons/svg 目录下的 SVG 文件并生成 symbols,用起来就像组件一样方便。

2. 传统位图雪碧图的生成

如果你还在维护旧项目或必须使用位图小图标,可以通过构建工具自动合成雪碧图。

  • Webpack:使用 webpack-spritesmith 插件,自动将文件夹内的 png 合成一张大图并生成 CSS/Less 代码。
  • 手动合成:在线工具如 SpriteSmith 上传图片打包下载。

生成后的 CSS 示例:

.icon {
  background-image: url('/sprite.png');
  background-repeat: no-repeat;
}
.icon-cart {
  background-position: -10px -50px;
  width: 24px;
  height: 24px;
}

3. 按需加载雪碧图

如果雪碧图本身较大(如包含了全站所有图标),可以考虑将其拆分为多个按页面或功能模块划分的雪碧图,避免一次加载过多无用图标。


小结:静态资源优化的原则是“能不加载就不加载,能少加载就少加载,能早加载就提前加载”。图片压缩和格式选择直接影响传输体积;字体优化通过子集化和格式选择大幅瘦身;雪碧图(尤其是 SVG 雪碧图)减少了请求数和闪烁问题。这些优化并不复杂,将相关配置项落进项目脚手架后,后续只需关心业务中新增的资源即可。