18. 资源压缩 – 图片与字体优化
概述
资源压缩是前端性能优化的重要环节,通过压缩图片、字体等静态资源,可以显著减少资源大小,加快页面加载速度,提升用户体验。
| What | 压缩图片、字体等静态资源 |
| Why | 减少资源大小,加快加载速度 |
| When | 有大量静态资源的项目 |
| Where | 构建配置、资源导入处 |
| Who | 需要优化资源加载的开发者 |
| How | 使用 imagemin、next/image、SVG 优化 |
1. 图片优化
1.1 图片格式选择
| JPEG | 有损压缩,不支持透明 | 照片、复杂图像 |
| PNG | 无损压缩,支持透明 | Logo、图标、简单图形 |
| WebP | 现代格式,体积更小 | 所有场景(浏览器支持) |
| AVIF | 最新格式,压缩率最高 | 所有场景(现代浏览器) |
| SVG | 矢量格式,无限缩放 | 图标、Logo、插图 |
// 使用 picture 元素提供多种格式
<picture>
<source srcSet="image.avif" type="image/avif" />
<source srcSet="image.webp" type="image/webp" />
<img src="image.jpg" alt="Description" />
</picture>
1.2 使用 Next.js Image 组件
import Image from 'next/image';
function OptimizedImage() {
return (
<Image
src="/large-image.jpg"
alt="Description"
width={800}
height={600}
priority={true} // 优先加载
loading="lazy" // 懒加载
sizes="(max-width: 768px) 100vw, 50vw"
quality={85} // 图片质量
/>
);
}
1.3 手动图片压缩工具
# 安装 imagemin CLI
npm install -g imagemin-cli
# 压缩图片
imagemin images/* –out-dir compressed-images
# 使用 sharp 库
npm install sharp
// sharp 压缩脚本
const sharp = require('sharp');
const fs = require('fs');
const path = require('path');
async function compressImage(inputPath, outputPath, options = {}) {
const {
width,
height,
quality = 80,
format = 'webp'
} = options;
let pipeline = sharp(inputPath);
if (width || height) {
pipeline = pipeline.resize(width, height, {
fit: 'cover',
position: 'center'
});
}
if (format === 'webp') {
pipeline = pipeline.webp({ quality });
} else if (format === 'avif') {
pipeline = pipeline.avif({ quality });
} else if (format === 'jpeg') {
pipeline = pipeline.jpeg({ quality });
}
await pipeline.toFile(outputPath);
console.log(`压缩完成: ${outputPath}`);
}
// 批量压缩
async function compressAll() {
const imagesDir = './src/images';
const outputDir = './src/images/compressed';
const files = fs.readdirSync(imagesDir);
for (const file of files) {
if (/\\.(jpg|jpeg|png)$/i.test(file)) {
const inputPath = path.join(imagesDir, file);
const outputPath = path.join(outputDir, file.replace(/\\.[^.]+$/, '.webp'));
await compressImage(inputPath, outputPath, { format: 'webp', quality: 80 });
}
}
}
2. Webpack 图片优化配置
2.1 使用 image-webpack-loader
npm install image-webpack-loader –save-dev
// webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\\.(jpe?g|png|gif|svg)$/i,
use: [
{
loader: 'file-loader',
options: {
name: '[name].[hash].[ext]',
outputPath: 'images',
},
},
{
loader: 'image-webpack-loader',
options: {
mozjpeg: {
progressive: true,
quality: 65,
},
optipng: {
enabled: true,
},
pngquant: {
quality: [0.65, 0.90],
speed: 4,
},
gifsicle: {
interlaced: false,
},
webp: {
quality: 75,
},
},
},
],
},
],
},
};
2.2 使用 url-loader(小图片内联)
// webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\\.(png|jpe?g|gif|svg)$/i,
use: [
{
loader: 'url-loader',
options: {
limit: 8192, // 8KB 以下的图片转为 Base64
name: '[name].[hash].[ext]',
outputPath: 'images',
fallback: 'file-loader',
},
},
],
},
],
},
};
3. Vite 图片优化
3.1 使用 vite-plugin-imagemin
npm install vite-plugin-imagemin –save-dev
// vite.config.js
import { defineConfig } from 'vite';
import viteImagemin from 'vite-plugin-imagemin';
export default defineConfig({
plugins: [
viteImagemin({
gifsicle: {
optimizationLevel: 7,
interlaced: false,
},
optipng: {
optimizationLevel: 7,
},
mozjpeg: {
quality: 80,
},
pngquant: {
quality: [0.8, 0.9],
speed: 4,
},
svgo: {
plugins: [
{
name: 'removeViewBox',
active: false,
},
{
name: 'removeEmptyAttrs',
active: true,
},
],
},
}),
],
});
3.2 自动生成 WebP
// vite.config.js
import { defineConfig } from 'vite';
import { ViteImageOptimizer } from 'vite-plugin-image-optimizer';
export default defineConfig({
plugins: [
ViteImageOptimizer({
test: /\\.(jpe?g|png|gif|tiff|webp|svg|avif)$/i,
exclude: undefined,
include: undefined,
includePublic: true,
logStats: true,
ansiColors: true,
svg: {
multipass: true,
plugins: [
{
name: 'preset-default',
params: {
overrides: {
cleanupNumericValues: false,
convertPathData: false,
mergePaths: false,
},
},
},
],
},
png: {
quality: 80,
},
jpeg: {
quality: 80,
},
jpg: {
quality: 80,
},
webp: {
lossless: false,
quality: 80,
},
avif: {
lossless: false,
quality: 80,
},
}),
],
});
4. SVG 优化
4.1 手动优化 SVG
# 使用 SVGO 优化 SVG
npm install -g svgo
# 优化单个文件
svgo input.svg -o output.svg
# 优化整个目录
svgo -f src/icons -o dist/icons
4.2 使用 SVGR 转换为 React 组件
npm install @svgr/webpack –save-dev
// webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\\.svg$/,
use: ['@svgr/webpack', 'url-loader'],
},
],
},
};
// 使用 SVG 作为 React 组件
import { ReactComponent as Icon } from './icon.svg';
function App() {
return <Icon className="icon" />;
}
4.3 使用 Vite 处理 SVG
// vite.config.js
import { defineConfig } from 'vite';
import svgr from 'vite-plugin-svgr';
export default defineConfig({
plugins: [svgr()],
});
// 使用
import Icon from './icon.svg?react';
function App() {
return <Icon className="icon" />;
}
5. 字体优化
5.1 字体格式选择
| WOFF2 | 压缩率最高 | 现代浏览器 |
| WOFF | 压缩率中等 | 广泛支持 |
| TTF | 未压缩 | 所有浏览器 |
| EOT | IE 专用 | 仅 IE |
5.2 字体子集化
# 使用 fonttools 创建字体子集
pip install fonttools
# 提取需要的字符
pyftsubset font.ttf \\
–text="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" \\
–output-file=font-subset.ttf
5.3 使用 Google Fonts 优化
<!– 预连接 –>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<!– 使用 display=swap 避免 FOIT –>
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap" rel="stylesheet" />
5.4 自托管字体配置
/* fonts.css */
@font-face {
font-family: 'CustomFont';
src: url('/fonts/custom-font.woff2') format('woff2'),
url('/fonts/custom-font.woff') format('woff');
font-weight: 400;
font-style: normal;
font-display: swap; /* 避免 FOIT */
}
@font-face {
font-family: 'CustomFont';
src: url('/fonts/custom-font-bold.woff2') format('woff2'),
url('/fonts/custom-font-bold.woff') format('woff');
font-weight: 700;
font-style: normal;
font-display: swap;
}
6. 懒加载图片
6.1 原生懒加载
// 使用 loading="lazy"
<img src="large-image.jpg" alt="Description" loading="lazy" />
// 使用 Intersection Observer
import { useEffect, useRef, useState } from 'react';
function LazyImage({ src, alt, placeholder }) {
const [imageSrc, setImageSrc] = useState(placeholder);
const imgRef = useRef(null);
useEffect(() => {
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
setImageSrc(src);
observer.disconnect();
}
});
});
if (imgRef.current) {
observer.observe(imgRef.current);
}
return () => observer.disconnect();
}, [src]);
return <img ref={imgRef} src={imageSrc} alt={alt} />;
}
6.2 React Lazy Load Image 组件
npm install react-lazy-load-image-component
import { LazyLoadImage } from 'react-lazy-load-image-component';
import 'react-lazy-load-image-component/src/effects/blur.css';
function ImageGallery() {
return (
<div>
<LazyLoadImage
src="large-image.jpg"
alt="Description"
effect="blur"
placeholderSrc="placeholder.jpg"
threshold={100}
width={800}
height={600}
/>
</div>
);
}
7. 完整示例:图片优化流程
// scripts/optimize-images.js
const sharp = require('sharp');
const fs = require('fs-extra');
const path = require('path');
const inputDir = './src/images/original';
const outputDir = './src/images/optimized';
async function optimizeImages() {
await fs.ensureDir(outputDir);
const files = await fs.readdir(inputDir);
for (const file of files) {
const inputPath = path.join(inputDir, file);
const ext = path.extname(file).toLowerCase();
const name = path.basename(file, ext);
if (['.jpg', '.jpeg', '.png'].includes(ext)) {
// 生成 WebP 版本
await sharp(inputPath)
.webp({ quality: 80 })
.toFile(path.join(outputDir, `${name}.webp`));
// 生成 AVIF 版本
await sharp(inputPath)
.avif({ quality: 70 })
.toFile(path.join(outputDir, `${name}.avif`));
// 生成不同尺寸
const sizes = [400, 800, 1200];
for (const size of sizes) {
await sharp(inputPath)
.resize(size, null, { withoutEnlargement: true })
.jpeg({ quality: 80 })
.toFile(path.join(outputDir, `${name}–${size}.jpg`));
}
}
}
console.log('图片优化完成!');
}
optimizeImages();
8. 总结
核心要点
| 图片格式 | 使用 WebP/AVIF 替代传统格式 |
| 压缩工具 | imagemin、sharp、image-webpack-loader |
| 懒加载 | loading=“lazy” + Intersection Observer |
| 字体优化 | WOFF2 格式 + 子集化 + font-display: swap |
资源优化检查清单
- 图片使用现代格式(WebP/AVIF)
- 图片已压缩
- 实现图片懒加载
- 使用响应式图片(srcset)
- SVG 已优化
- 字体使用 WOFF2 格式
- 配置 font-display: swap
- 小图片内联为 Base64
记忆口诀
图片压缩格式新,WebP AVIF 来替代 懒加载交互相应快,字体子集体积小 响应式图 srcset,不同设备都适用
9. 相关资源
- Sharp 文档
- WebP 介绍
- AVIF 介绍
- Google Fonts 优化