图片是内容页最重的资源,也是最大内容绘制(LCP)最常见的祸首。next/image 自动做响应式尺寸、现代格式、懒加载、CDN 分发,全部免费。但 Next.js 16(默认打包器已换成 Turbopack,截至 2026 年 6 月当前线是 16.2)动了三个默认值,从 14/15 升上来的站会被悄悄坑到:priority 被废弃、qualities 改成配置里必填、缓存 TTL 从原来的长默认值掉到了 4 小时。本文给出能跑的配置,外加要修的几处具体破坏点。
一句话总结
- LCP 图用
preload,别再用priority——后者在 Next.js 16 已废弃。 next.config.js里加上qualities——Next.js 16 起必填,否则优化器返回400 Bad Request。- 设
formats: ['image/avif', 'image/webp']——AVIF 需手动开(默认只有 WebP),体积小约 20%。 - 永远配上
sizes,让浏览器按布局拉对应宽度,而不是deviceSizes里最大那个。 - 把
minimumCacheTTL从新的 4 小时默认值(14400)调长一点,如果你的图很稳定。
Next.js 16 改了什么
如果你的配置和 <Image> 写法是按 Next.js 14 或 15 写的,有三处行为现在变了(依据官方版本历史 v16.0.0):
| 项目 | Next.js 15 | Next.js 16(截至 2026 年 6 月) |
|---|---|---|
| LCP 启用属性 | priority | preload(priority 已废弃) |
qualities 配置 | 可选 | 必填;默认 [75] |
minimumCacheTTL 默认 | 长寿命 | 14400(4 小时) |
formats 默认 | ['image/webp'] | ['image/webp'](AVIF 仍需手动开) |
| 磁盘缓存上限 | 无 | maximumDiskCacheSize,默认取空闲磁盘的 50% |
实际后果:去年能跑的配置,现在可能在 quality 取值上报错,或者每 4 小时就把已优化的图重新拉一遍。
怎么判断有问题
- PageSpeed Insights 提示 “Properly size images” 或 “Serve images in next-gen formats”。
- LCP 元素是 hero 图,移动端 LCP 超过 2.5 秒(Core Web Vitals 里”良好”的阈值)。
- 升级后优化器返回
400 Bad Request——多半是qualities现在强制生效,而你用了不在列表里的 quality 值。 - 部署时
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"
preload // 只有 LCP 图加 —— 见第 2 步
/>
);
}
-
LCP 图加
preload,不是priority。 Next.js 16 里priority已废弃,改用preload,它会在<head>插一个<link rel="preload">。一页只标一张——通常就是 hero,绝不是 footer logo。官方文档提醒:当不同视口下 LCP 元素会变时,优先用fetchPriority="high"而非preload,免得预加载了某些断点下其实隐藏的图。 -
配好
images块。 这份配置把 Next.js 16 在意的三件事都设上了:
// next.config.mjs
/** @type {import('next').NextConfig} */
const nextConfig = {
images: {
formats: ['image/avif', 'image/webp'], // AVIF 需手动开;默认只有 webp
qualities: [50, 75, 90], // Next.js 16 必填 —— quality 属性须命中
deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
imageSizes: [32, 48, 64, 96, 128, 256, 384],
minimumCacheTTL: 2678400, // 31 天;默认现在是 14400(4 小时)
remotePatterns: [
{ protocol: 'https', hostname: 'images.unsplash.com', pathname: '/photos/**' },
{ protocol: 'https', hostname: 'cdn.yourdomain.com', pathname: '/**' },
],
},
};
export default nextConfig;
不在 qualities 数组里的 quality 属性,在组件里会就近取最接近的允许值;但如果直接命中优化器 API 而 quality 不在列表里,会返回 400。这个列表保持短而明确。
- 远程图要配精准的
remotePatterns。 hostname 别用裸**。Next.js 16 文档警告:省略protocol、port、pathname或search会隐式变成通配,让人能把任意图片塞进你的优化器。也可以用简写new URL('https://example.com/account123/**')。自己/public下的图,用localPatterns锁住:
images: {
localPatterns: [
{ pathname: '/assets/images/**', search: '' },
],
},
sizes决定拉哪个宽度。 不设的话,Next.js 只生成1x/2x的srcset;设了才有完整响应式集(640w、750w……),浏览器才能挑对。常见文章 hero(移动满宽、桌面约 800px)的示例:
<Image
src={heroImg}
alt="..."
sizes="(max-width: 640px) 100vw,
(max-width: 1024px) 80vw,
800px"
preload
/>
fill必须配定位父元素(relative、absolute或fixed),否则图高度坍成 0。宽高比不固定时用它:
<div style={{ position: 'relative', aspectRatio: '16/9' }}>
<Image src="/cover.jpg" alt="..." fill style={{ objectFit: 'cover' }} sizes="100vw" />
</div>
- 非 Vercel host 要选 loader 策略。 Vercel 上优化在边缘跑;其它地方你得跑 Node 服务(请求时优化)、用自定义 loader,或者预 build 好资源。自定义 loader 把优化交给 Cloudflare Images、Imgix 或自家代理:
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,<head>里有 preload 链接。终端也能查:
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=2678400
- 测 LCP 影响。 Lighthouse 会指出实际的 LCP 元素:
npx lighthouse https://yourdomain.com/articles/foo/ \
--only-categories=performance --chrome-flags=--headless --quiet \
| grep -A1 'largest-contentful-paint\|LCP element'
AVIF 还是 WebP:真实权衡
Next.js 文档说得很直白:AVIF 编码大约慢 50%,但比 WebP 小约 20%。对内容站有两个后果:
- 首次请求某张图更慢(优化器按需编码);之后每次命中缓存的请求都更快更轻。
- 每种格式分开缓存,所以同时开 AVIF 和 WebP,已优化图的存储约翻倍。配上新的
maximumDiskCacheSizeLRU 淘汰,一般没事——但在小 VPS 上要设上限,或干脆只用 WebP。
博客的照片类 hero 图,AVIF 值得开。扁平色的 UI 截图,WebP-only 默认往往看不出差别,且编码更快。
容易踩的坑
- 升到 Next.js 16 后还在用
priority。能跑但已废弃,迁到preload。 - 配置里忘了
qualities,结果用了未列出的 quality 值,优化器返回400 Bad Request。 - 站点图片稳定且带内容 hash,却把
minimumCacheTTL留在新的 4 小时默认值——重新优化的次数远超需要。 remotePatterns的 hostname 通配**——任何域名都成优化目标,成本和攻击面飞涨。fill没配定位父元素,图缩成零高。- 不设
sizes,把 2000px 宽的图发给 400px 宽的手机栏。 - 一张烂图就全站
unoptimized: true——next/image的意义全没了。
这篇适合谁
所有发图的 Next.js 站——博客、营销页、产品图册、带图文档站——尤其是刚升到 Next.js 16、还没审过 images 配置的人。
FAQ
- Next.js 16 把
priority删了吗?:没删,只是废弃——还能用但会打警告。LCP 图换成preload;当 LCP 元素随视口变时,用fetchPriority="high"。 - 升级后优化器为什么返回 400?:Next.js 16 把
qualities改成必填。如果你<Image quality={...}>的值不在允许列表(默认[75])里,直接命中优化器会返回400。把你用到的值加进images.qualities。 next/image在 Vercel 外能用吗?:能。配 Node 服务做请求时优化;用自定义 loader 把活交给 Cloudflare Images 或 Imgix。静态导出(output: 'export')需要外部 loader 或unoptimized: true。- 该开 AVIF 吗?:照片类内容值得——比 WebP 小约 20%。首次请求编码慢约 50%,缓存存储翻倍,所以扁平的 UI 截图用 WebP 默认往往就够。
- SVG 怎么办?:
next/image不优化 SVG,安全考虑默认禁用。非要用就设dangerouslyAllowSVG加严格的contentSecurityPolicy;否则内联 SVG 或用普通<img>。