Image 组件:自动优化图片加载
第17章:Image 组件:自动优化图片加载
仅通过替换一个 HTML 标签(
→
),就能获得格式转换、尺寸调整、懒加载、防 CLS 四重优化,LCP 通常改善 200-500ms。
本章核心问题:next/image 自动做了哪些优化?priority、sizes、fill 如何影响性能?
读完本章你将理解:
- 自动格式转换(AVIF/WebP)、尺寸调整、懒加载的工作原理
- priority 对 LCP 图片的关键作用与滥用风险
- sizes 属性对响应式图片的精确控制
Level 1 · 你需要知道的(1-3年经验)
为什么不直接用 <img> 标签
HTML 原生 <img> 标签在现代 Web 性能优化的角度存在几个固有缺陷:
格式问题:浏览器原生支持 WebP 和 AVIF 这两种高效格式,但大多数图片服务仍以 JPEG/PNG 格式存储。AVIF 的压缩率通常比 JPEG 高 50% 以上,在相同质量下体积更小。手动转换每张图片不现实,格式的选择还需要检测浏览器支持情况。
尺寸问题:移动设备屏幕宽度通常在 375-414px,但服务器经常为其提供 2000px 宽的原图。用户下载了 10 倍于实际需要的数据量。
CLS(Cumulative Layout Shift)问题:没有 width 和 height 属性的 <img> 标签在图片加载完成前占据 0 高度,加载后撑开布局,导致页面"跳动"。这直接影响 Core Web Vitals 中的 CLS 指标。
懒加载问题:首屏以下的图片不应该在页面加载时立即请求,但 <img> 标签默认立即加载所有图片。
next/image 的 Image 组件解决了上述所有问题,且大部分功能开箱即用无需配置。
基础用法与自动优化
import Image from 'next/image'
export default function ProductCard() {
return (
<Image
src="/products/shoe.jpg"
alt="Nike Air Max 运动鞋"
width={800}
height={600}
className="rounded-lg"
/>
)
}
这几行代码触发了以下优化:
- 格式转换:Next.js 检测浏览器支持情况,优先提供 AVIF,其次 WebP,最后降级到原始格式。
- 尺寸调整:根据
widthprop 生成适合实际渲染尺寸的版本,避免传输超大图片。 - 懒加载:默认开启,视口外的图片不会立即请求。
- 防 CLS:根据
width和height计算宽高比,在图片加载前预留空间。
优化是按需进行的:第一次请求某个尺寸的图片时,Next.js 实时处理并缓存结果。后续请求直接从缓存读取。
fill 模式:容器驱动的图片尺寸
当图片需要填满父容器时,使用 fill prop 而不是固定的 width/height:
import Image from 'next/image'
export function CoverImage({ src, alt }: { src: string; alt: string }) {
return (
<div className="relative w-full aspect-video">
<Image
src={src}
alt={alt}
fill
className="object-cover"
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
/>
</div>
)
}
使用 fill 时有几个要点:
- 父容器必须设置
position: relative(或absolute/fixed) - 父容器必须有明确的尺寸(不能是
height: auto) - 强烈建议同时提供
sizes属性,否则 Next.js 会假设图片占满 100vw
object-cover 类让图片覆盖整个容器并裁剪超出部分,object-contain 则让图片完整展示并留白。
Level 2 · 它是怎么运行的(3-5年经验)
priority 属性:LCP 图片的关键优化
懒加载是好事,但首屏最大内容(LCP 元素)不应该懒加载——懒加载反而会延迟 LCP。priority prop 告诉 Next.js 这张图片是高优先级的:
// 首页英雄图:一定要加 priority
<Image
src="/hero.jpg"
alt="网站主视觉"
width={1920}
height={1080}
priority
/>
priority 做了两件事:
- 禁用懒加载,图片立即开始加载
- 在 HTML
<head>中添加<link rel="preload">预加载提示,让浏览器更早发现并开始下载这张图片
实测影响:在生产项目中,为首屏英雄图添加 priority 通常能将 LCP 改善 200-500ms。Lighthouse 报告会警告"Image elements do not have explicit width and height"或"Largest Contentful Paint image was not preloaded",这两个问题都通过正确使用 next/image 解决。
何时不用 priority:绝大多数图片不在首屏内,不应加 priority。为所有图片添加 priority 会让优先提示失去意义,反而增加初始负载。经验法则:通常每个页面只有 1-2 张图片需要 priority。
sizes 属性:响应式图片的精确控制
sizes 属性是 next/image 中最容易被忽视但对性能影响最大的属性。它告诉浏览器(和 Next.js 的图片优化器)在不同视口宽度下,图片将占据多少屏幕宽度:
// 响应式布局:手机全宽,平板半宽,桌面三分之一宽
<Image
src="/product.jpg"
alt="产品图"
fill
sizes="(max-width: 640px) 100vw,
(max-width: 1024px) 50vw,
33vw"
/>
没有 sizes 属性时,浏览器假设图片占满 100vw,会请求全宽版本的图片。对于桌面端只占 33% 宽度的图片,这意味着下载了 3 倍于实际需要的数据。
Next.js 根据 sizes 和内置的断点生成 srcset:
<!-- Next.js 生成的 HTML -->
<img
srcset="
/_next/image?url=%2Fproduct.jpg&w=640&q=75 640w,
/_next/image?url=%2Fproduct.jpg&w=828&q=75 828w,
/_next/image?url=%2Fproduct.jpg&w=1080&q=75 1080w
"
sizes="(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 33vw"
...
/>
浏览器根据当前视口宽度和 sizes 计算所需图片宽度,再从 srcset 中选择最合适的版本。
远程图片域名配置
使用来自外部域名的图片时,需要在 next.config.ts 中明确授权,防止滥用图片优化服务:
// next.config.ts
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
images: {
remotePatterns: [
{
protocol: 'https',
hostname: 'cdn.example.com',
port: '',
pathname: '/images/**',
},
{
protocol: 'https',
hostname: '**.cloudinary.com', // 通配符匹配子域名
},
{
protocol: 'https',
hostname: 'images.unsplash.com',
},
],
},
}
export default nextConfig
未在 remotePatterns 中授权的域名图片会返回 400 错误。** 通配符匹配任意子域名,* 匹配单级路径段。
Level 3 · 规范怎么定义的(资深)
自定义 Loader:对接 CDN
如果你使用 Cloudinary、Imgix、Akamai 等专业图片 CDN,可以通过 loader 属性完全控制图片 URL 的生成方式:
// lib/imageLoader.ts
import type { ImageLoaderProps } from 'next/image'
export function cloudinaryLoader({ src, width, quality }: ImageLoaderProps) {
const params = [
'f_auto', // 自动格式选择(AVIF/WebP/JPEG)
'c_limit', // 限制尺寸不超过原图
`w_${width}`, // 宽度
`q_${quality ?? 75}`, // 质量
].join(',')
return `https://res.cloudinary.com/mycloud/image/upload/${params}/${src}`
}
// 使用自定义 loader
import Image from 'next/image'
import { cloudinaryLoader } from '@/lib/imageLoader'
export function CloudinaryImage({
publicId,
alt,
width,
height,
}: {
publicId: string
alt: string
width: number
height: number
}) {
return (
<Image
loader={cloudinaryLoader}
src={publicId}
alt={alt}
width={width}
height={height}
/>
)
}
自定义 loader 完全接管了 URL 生成,Next.js 内置的图片优化器不再参与,所有格式转换和尺寸调整由 CDN 负责。这在已有专业图片处理服务的情况下是更好的选择,避免重复优化。
也可以在 next.config.ts 中全局配置 loader:
// next.config.ts
const nextConfig: NextConfig = {
images: {
loader: 'custom',
loaderFile: './lib/imageLoader.ts',
},
}
模糊占位符:改善加载体验
placeholder="blur" 在图片加载完成前显示模糊预览,避免白色空白区域:
// 本地图片:Next.js 在构建时自动生成 blurDataURL
import heroImage from '@/public/hero.jpg'
<Image
src={heroImage}
alt="英雄图"
placeholder="blur"
priority
/>
// 远程图片:需要手动提供 blurDataURL
// 通常是一个 10x10 像素的 base64 编码图片
<Image
src="https://cdn.example.com/photo.jpg"
alt="照片"
width={800}
height={600}
placeholder="blur"
blurDataURL="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQ..."
/>
生成远程图片的 blurDataURL,可以在构建时从图片 URL 获取,或使用 plaiceholder 库:
// scripts/generateBlurData.ts
import { getPlaiceholder } from 'plaiceholder'
export async function getBlurDataUrl(url: string) {
const { base64 } = await getPlaiceholder(url)
return base64
}
图片优化对 LCP 的实际影响
以一个真实场景为例:一个电商首页,主要英雄图为 2.4MB 的 JPEG(3000x2000 像素)。
优化前(原生 <img>):
- 下载:2.4MB,LCP: 4.2 秒,CLS: 0.18(布局抖动),Lighthouse Performance: 42 分
优化后(next/image + priority + sizes):
- 桌面端下载 WebP,约 340KB(-86%)
- 移动端下载 AVIF,约 120KB(-95%)
- LCP: 1.4 秒(-67%),CLS: 0(预留空间),Lighthouse Performance: 87 分
数字差距来自三个叠加效应:更现代的格式(AVIF 压缩率)、更合适的尺寸(不再下载 3000px 宽的图给 375px 的手机)、以及预加载(priority 让浏览器提前发现图片)。
常见错误与最佳实践
错误一:对所有图片加 priority。优先级标识失去意义,页面初始加载变慢。只对 LCP 图片加。
错误二:不设置 sizes。对于 fill 模式的图片,Next.js 默认生成按 100vw 尺寸的图片,即使图片只占容器的 1/3 宽度。
错误三:在 src 中拼接动态 URL 时不处理空值。src 为空字符串会导致错误请求。
// 错误
<Image src={user.avatar} alt={user.name} ... />
// 正确:提供默认值
<Image
src={user.avatar ?? '/default-avatar.png'}
alt={user.name}
...
/>
错误四:在 CSS 中用 max-width: 100% 覆盖 Next.js 的内联样式。next/image 会设置内联 width 和 height 样式,用 CSS !important 覆盖会破坏布局计算。
Level 4 · 边界与陷阱(所有人)
陷阱1:对所有图片加 priority 会让优先级标识失去意义——每个页面通常只有 1-2 张图片需要 priority。
陷阱2:不设置 sizes 时,fill 模式的图片默认按 100vw 生成——桌面端只占 33% 宽度的图片会下载 3 倍于实际需要的数据。
陷阱3:Image 的 src 为空字符串会导致错误请求——动态 URL 时务必提供默认值。
小结
next/image 是少数几个能在不改变产品功能的前提下,仅通过替换一个 HTML 标签就带来显著性能提升的优化手段。理解 priority 与懒加载的关系、sizes 对 srcset 生成的影响、以及 fill 模式的使用条件,是发挥 next/image 全部潜力的关键。对于使用外部图片 CDN 的项目,自定义 loader 让图片处理能力更上一层楼。