图片优化策略深度解析:格式选型、懒加载与响应式图片实战
图片占页面总流量 60-80%,一张优化过的 Hero 图能从 2.5MB 降到 35KB 而肉眼难辨,是投入产出比最高的性能方向。 面试常考格式选型决策树(WebP、AVIF)、懒加载、响应式图片 srcset、sizes 与 CDN 压缩策略。
一句话概括
图片占页面总流量 60-80%,一张优化过的 Hero 图能从 2.5MB 降到 35KB(98% 节省),而且肉眼几乎看不出区别——这是所有性能优化中投入产出比最高的方向。
核心知识点
1. 格式选择决策树
1
2
3
4
5
6
7
8
9
// 当前主流格式的实用选择
// 照片(有损)→ AVIF > WebP > MozJPEG
// 图标/插画(少色)→ SVG > PNG-8
// 需要透明度 → WebP > PNG
// 动图 → WebP 动图 > GIF
// 绝对保真 → PNG
// 格式体积对比(同一张 1200px 照片)
// JPEG: 180KB | WebP: 95KB (-47%) | AVIF: 60KB (-67%)
1
2
3
4
5
6
7
8
9
10
<!-- 最完整的响应式图片写法 -->
<picture>
<source type="image/avif" srcset="hero-480.avif 480w, hero-1200.avif 1200w"
sizes="(max-width: 768px) 100vw, 1200px">
<source type="image/webp" srcset="hero-480.webp 480w, hero-1200.webp 1200w"
sizes="(max-width: 768px) 100vw, 1200px">
<img src="hero-fallback.jpg" alt="banner" width="1200" height="600"
loading="eager" fetchpriority="high" decoding="async"
style="width:100%; height:auto; aspect-ratio:2/1">
</picture>
srcset 让浏览器根据设备 DPR 自动选图,sizes 告诉浏览器图片在布局中占多大空间,width/height 避免 CLS(布局偏移)。
2. 懒加载的正确姿势
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!-- 原生方式(最简单,2026 年支持率 > 95%) -->
<img src="product.jpg" loading="lazy" alt="商品" width="300" height="200">
<!-- 浏览器在图片距离视口约 1250px 时开始加载,完全不需要 JS -->
<!-- 精细控制:IntersectionObserver -->
<script>
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
img.onload = () => img.classList.add('loaded');
observer.unobserve(img);
}
});
}, { rootMargin: '200px' }); // 提前 200px 开始加载
document.querySelectorAll('img[data-src]').forEach(img => observer.observe(img));
</script>
关键原则:首屏图片用 loading="eager"(或用 fetchpriority="high"),非首屏图片用 loading="lazy" + data-src,始终设置 width/height 避免 CLS。
3. 占位策略:LQIP(低质量图片占位)
1
2
3
4
5
6
7
8
9
<!-- 3 字节的占位比 3 秒的白屏好一万倍 -->
<img src="data:image/jpeg;base64,/9j/4AAQSkZJRg..." <!-- ~400 字节的模糊缩略图 -->
data-src="/product-full.webp" alt="商品"
class="lqip" width="400" height="500">
<style>
.lqip { filter: blur(20px); transform: scale(1.1); transition: filter 0.4s; }
.lqip.loaded { filter: blur(0); }
</style>
渐进式加载的体验:先看到模糊的色块(LQIP)→ 0.5s 后清晰 → 用户知道”这块在加载”,而不是”页面卡死了”。
4. 构建时自动优化管线
1
2
3
4
5
6
7
8
9
10
11
12
import sharp from 'sharp';
import { glob } from 'glob';
// 一行脚本批量处理所有图片
for (const file of await glob('src/images/**/*.{jpg,png}')) {
const name = file.replace(/\.\w+$/, '');
for (const w of [480, 768, 1200]) {
await sharp(file).resize(w).webp({ quality: 75 }).toFile(`${name}_${w}.webp`);
await sharp(file).resize(w).avif({ quality: 65 }).toFile(`${name}_${w}.avif`);
}
}
// 一张 2.5MB 的原图 → 8 个小文件(WebP+AVIF),最大的才 40KB
5. WebP vs AVIF 的隐藏成本
1
2
3
4
5
6
7
8
9
// AVIF 体积比 WebP 再小 25-35%,但解码慢 2-3 倍
// 不是所有场景都该用 AVIF:
// ✅ AVIF:大图照片、CDN 批量分发、高端设备用户
// ✅ WebP:缩略图、低端设备、需要快速渲染的列表图
// ✅ JPEG:兜底(iOS 14 以下、旧安卓 WebView)
// 如果用户是 2G 网络 → 体积优先,用 AVIF
// 如果用户千兆 WiFi + 低端机 → 解码速度优先,用 WebP
其实你每天都在用
- 微信发图自动压缩:你发原图前微信先压成 300KB——这就是服务端图片处理,和 sharp 做的事一样
- 朋友圈模糊变清晰:打开朋友圈图片先从模糊到清晰——LQIP + 渐进式加载
- 淘宝商品列表:往下滑时图片才一张张出来——
loading="lazy"或 IntersectionObserver 懒加载 - 浏览器自动选图:手机访问一个网站下载 480px 的图,电脑访问同一个 URL 下载 1200px 的图——
srcset+sizes在工作
常见误解(FAQ)
❌ 误区:WebP 已经完全替代 JPEG 了,不需要 JPEG 兜底
Safari 14 以下(iOS 14 以下设备)、部分安卓 WebView 仍然不支持 WebP。2026 年这些设备的占有率虽低(< 3%),但如果你的用户群包含大量旧设备用户,JPEG 兜底仍然是必要的。
❌ 误区:图片用 CDN 了就不需要压缩
CDN 加速的是传输,不改变文件大小。一张 2.5MB 的图即使从最近的 CDN 节点下发,在 4G 网络上也要 3 秒以上——CDN + 压缩一起才有效果。
❌ 误区:SVG 是小文件,不需要优化
复杂的 SVG(如地图、含大量路径的插画)可能达到 500KB 以上。SVG 要用 SVGO 压缩(移除元数据、合并路径、精简精度),通常能减少 30-60%。
❌ 误区:渐进式 JPEG 一定比基线 JPEG 好
渐进式 JPEG 在慢网络上体验更好(先模糊后清晰),但解码需要更多内存。对于小于 10KB 的小图,基线 JPEG 反而更快。
一句话总结
图片优化的”不可能三角”是体积、质量和解码速度——AVIF 体积最小但解码最慢,WebP 是当前最均衡的选择,而 JPEG 永远是那个永远不会出错的兜底方案。