图片是内容页最重的资源,也是 LCP 最常见的祸首。next/image 自动做响应式尺寸、现代格式、懒加载、CDN 分发。开箱够用,但默认值在远程图、LCP 优先、host 配置上仍会让人栽。
问题背景
Next.js 15 的 next/image 用 sharp 优化、自动 AVIF / WebP、blur 占位、内置懒加载。Vercel 上优化在边缘跑。其它 host 要么跑 Node 服务、要么配 loader、要么预 build 优化好的资源。先把 host 这条理顺。
判断标准
- PageSpeed Insights 提示 “Properly size images” 或 “Serve images in next-gen formats”。
- LCP 元素是 hero 图,移动端 LCP 超过 2.5 秒。
- 部署时
next/image报 “Invalid src prop, hostname is not configured”。 - 看到 CLS 来自文字之后图片才加载。
实操步骤
<img>换成next/image。静态 import 的图自动推断宽高,自带 blur 占位:
import Image from 'next/image';
import heroImg from '@/public/hero.png';
export default function Page() {
return (
<Image
src={heroImg}
alt="周报指标仪表盘"
placeholder="blur"
sizes="(max-width: 768px) 100vw, 800px"
priority // 只有 LCP 图加 —— 见第 2 步
/>
);
}
-
LCP 图加
priority。 关掉懒加载,加上fetchpriority="high"。一页最多一两张图标——通常就是 hero,绝不是 footer logo。 -
远程图必须配
remotePatterns。 精准指定,别用**:
// next.config.mjs
/** @type {import('next').NextConfig} */
const nextConfig = {
images: {
formats: ['image/avif', 'image/webp'],
deviceSizes: [640, 750, 828, 1080, 1200, 1920],
imageSizes: [16, 32, 64, 96, 128, 256, 384],
minimumCacheTTL: 31536000, // 1 年 —— 资源带内容 hash
remotePatterns: [
{ protocol: 'https', hostname: 'images.unsplash.com', pathname: '/photos/**' },
{ protocol: 'https', hostname: 'cdn.yourdomain.com', pathname: '/**' },
],
},
};
export default nextConfig;
sizes很关键。 不设的话浏览器会按deviceSizes里最大那个拉,与布局无关。常见文章 hero(移动满宽、桌面约 800px)的示例:
<Image
src={heroImg}
alt="..."
sizes="(max-width: 640px) 100vw,
(max-width: 1024px) 80vw,
800px"
priority
/>
fill必须配定位父元素,否则图高度坍成 0。宽高比不固定时必须用:
<div style={{ position: 'relative', aspectRatio: '16/9' }}>
<Image src="/cover.jpg" alt="..." fill style={{ objectFit: 'cover' }} sizes="100vw" />
</div>
- 非 Vercel host 要选 loader 策略。静态 export 强制要么自定义 loader、要么
unoptimized:
// Cloudflare Images / Imgix / 自定义 loader
const nextConfig = {
images: {
loader: 'custom',
loaderFile: './lib/image-loader.ts',
},
};
// lib/image-loader.ts
export default function loader({ src, width, quality }) {
return `https://imgproxy.yourdomain.com/${width}/q${quality ?? 75}${src}`;
}
完全静态导出,放弃优化换可移植:
const nextConfig = {
output: 'export',
images: { unoptimized: true },
};
- DevTools Network 验证。hero 图应该只请求一次,content-type 是
image/avif,fetchpriority: high:
# 终端也能查
curl -sI 'https://yourdomain.com/_next/image?url=/hero.png&w=1080&q=75' \
| grep -iE 'content-type|cache-control'
# content-type: image/avif
# cache-control: public, max-age=31536000, immutable
- 测 LCP 回归。Lighthouse 会指出实际的 LCP 元素:
npx lighthouse https://yourdomain.com/articles/foo/ \
--only-categories=performance --chrome-flags=--headless --quiet \
| grep -A1 'largest-contentful-paint\|LCP element'
容易踩的坑
- LCP 图忘加
priority——懒加载推迟最重要的请求,LCP 直接崩。 images.remotePatterns通配**——任何域名都成优化目标,攻击面和成本飞涨。fill没配定位父元素(relative/absolute/fixed),图缩成零高。- 不设
sizes,把 2000px 宽的图发给 400px 宽的手机栏。 - 非 Vercel host 没配 loader 还在问”为什么优化没生效”。
- 一张烂图就全站
unoptimized: true——next/image的意义全没了。
这篇适合谁
所有发图的 Next.js 站——博客、营销页、产品图册、带图文档站。
这篇不适合谁
纯文字零图的站(罕见)。仍建议把 next/image 默认配好以备后用。
FAQ
next/image在 Vercel 外能用吗?: 能——配 Node 服务做请求时优化,或者用自定义 loader(Cloudflare、Imgix)。静态 export 需要外部 loader。- AVIF 比 WebP 值得吗?: 照片类值得——同等质量小 20-30%。
next/image会按浏览器支持度发 AVIF。 - 该用 blur 占位吗?: 首屏图值得——减少感知加载时间。静态 import 自动有。
- SVG 怎么办?: 内联或用
<img>。next/image不优化 SVG,安全考虑默认禁用。