静态资源往往是页面体积的“大头”——一张未压缩的高清图片可能比整个应用的 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. 响应式图片 + 懒加载
不同屏幕尺寸不需要加载同一张大图。结合 srcset 和 sizes 属性,浏览器会根据设备像素比选择合适的图片:
<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. 字体子集化
中文或日文字体文件通常很大,但实际用到的字符可能只有几百个。使用工具删除未使用的字形,能大幅瘦身。
如果项目中只有固定的字符(如公司名称、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 雪碧图)减少了请求数和闪烁问题。这些优化并不复杂,将相关配置项落进项目脚手架后,后续只需关心业务中新增的资源即可。