图像是网页最大的字节来源。下面是一份能让 Lighthouse 分数从 60 涨到 95
的实战清单。
1. 用对格式
- AVIF:2024 主流浏览器都支持,文件最小,复杂图比 WebP 还小 20-30%
- WebP:兼容性更广(IE11 之外都行),编码快
- JPEG:照片,进度加载支持好
- PNG:透明背景、像素图 / icon
- SVG:矢量图(logo、icon)
服务端按 Accept 头返回最佳格式:
<picture>
<source srcset="hero.avif" type="image/avif">
<source srcset="hero.webp" type="image/webp">
<img src="hero.jpg" alt="Hero" width="1200" height="600" loading="lazy">
</picture>
浏览器从上到下选第一个支持的。<img> 是兜底。
2. 永远写 width / height
<img src="thumb.jpg" alt="" width="320" height="180">
让浏览器在加载图片前知道占位空间,避免 CLS(Cumulative Layout Shift)。
没写的图片加载完会"撑开"页面跳动。
CSS 控制实际尺寸:
img { max-width: 100%; height: auto; }
但 HTML 的 width/height 用来告诉浏览器宽高比(aspect ratio),必加。
3. responsive:不同视口不同尺寸
<img
src="hero-800.jpg"
srcset="hero-400.jpg 400w, hero-800.jpg 800w, hero-1600.jpg 1600w"
sizes="(max-width: 600px) 100vw, (max-width: 1200px) 50vw, 33vw"
alt="Hero"
width="1600" height="800"
>
srcset 提供多分辨率,sizes 告诉浏览器图片实际显示大小,
浏览器结合 device pixel ratio 选最优 src。
4. lazy loading(原生属性)
<img src="below-fold.jpg" loading="lazy" alt="" width="..." height="...">
<iframe src="..." loading="lazy"></iframe>
loading="lazy" 让浏览器在元素进入视口前不加载。
首屏 / above-the-fold 的图片不要加(用 loading="eager" 默认)。
5. decoding="async"
<img src="x.jpg" loading="lazy" decoding="async">
让浏览器异步解码图片,不阻塞主线程。
6. priority hints
首屏关键图加 fetchpriority="high":
<img src="hero.jpg" fetchpriority="high" loading="eager">
预加载 + 高优先级,关键内容更快出现。LCP(Largest Contentful Paint)
指标提升明显。
7. preload 关键资源
<link rel="preload" as="image"
href="hero.jpg"
imagesrcset="hero-800.jpg 800w, hero-1600.jpg 1600w"
imagesizes="100vw">
页面 head 里 preload 首屏大图,浏览器立刻并行下载。
8. CDN 上做实时转换
https://your-cdn.example.com/image?src=foo.jpg&w=800&fmt=avif
CDN 收到请求后按参数生成对应格式 / 尺寸,缓存返回。
服务:Cloudinary、imgix、Cloudflare Images、Bunny.net、自己用 imgproxy
搭建。
9. 工具:批量优化
# AVIF
npm install -g sharp-cli
sharp -i 'images/*.jpg' -o 'dist/' avif --quality 60
# WebP
cwebp -q 80 input.png -o output.webp
# JPEG 进度 + mozjpeg 优化
mozjpeg -quality 80 -progressive -outfile out.jpg in.jpg
# PNG 优化
pngquant --quality=70-90 in.png # 有损但视觉无差
oxipng --opt 4 in.png # 无损
# SVG
svgo -i in.svg -o out.svg
CI 阶段把 source 全转一遍。
10. Next / Vite 框架内置
Next.js
import Image from 'next/image'
<Image
src="/hero.jpg"
width={1200} height={600}
alt="Hero"
priority // 首屏 LCP
placeholder="blur"
/>
next/image 自动生成多分辨率 + AVIF / WebP + 懒加载。
Vite
vite-imagetools plugin:
import hero from './hero.jpg?w=400;800;1600&format=avif;webp;jpg&as=picture'
<img {...hero} loading="lazy" />
Build 时自动生成所有尺寸 / 格式 + 注入 srcset。
11. 模糊占位符(LQIP)
预生成 base64 编码的极小图当占位:
<img src="data:image/jpeg;base64,/9j/..." />
<!-- 同时异步加载真图,加载完替换 -->
或者用 BlurHash 算法(更紧凑,几十字节)。Next.js placeholder="blur"
自动做这个。
12. SVG sprite(多个 icon)
<svg style="display:none">
<symbol id="icon-cart" viewBox="0 0 24 24">
<path d="..." />
</symbol>
</svg>
<!-- 使用 -->
<svg><use href="#icon-cart" /></svg>
一次下载所有 icon,按需引用,比 icon font 性能 / 灵活性都好。
13. 验证
Lighthouse / WebPageTest 跑一遍:
- LCP < 2.5s ✅
- CLS < 0.1 ✅
- 没有 "Properly size images" 警告
- 没有 "Serve images in next-gen formats" 警告
14. 视频替代 GIF
GIF 巨大 + 不可压缩。替代:
<video autoplay loop muted playsinline>
<source src="anim.webm" type="video/webm">
<source src="anim.mp4" type="video/mp4">
</video>
同一段动画 WebM 通常比 GIF 小 80-90%。
踩过的坑
- 用
<img src=但没写width/height→ CLS 0.3+ 直接红色。 - AVIF 编码慢(CPU 几秒一张):build 时缓存输出,不要每次重编码。
- responsive
srcset算错sizes→ 浏览器选了过大或过小的图。
开 DevTools 看 Network 实际请求的 URL 校验。 - "压缩太狠" → JPEG quality 60 以下肉眼可见 artifact。建议 70-85
甜点。
登录后参与评论。