Next.js 图片优化:升级到 16 的检查清单

next/image 是你白拿的最大性能收益——但 Next.js 16 把 priority 标记为废弃、qualities 改成必填、缓存默认降到 4 小时。本文讲清楚要改什么。

图片是内容页最重的资源,也是最大内容绘制(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 15Next.js 16(截至 2026 年 6 月)
LCP 启用属性prioritypreloadpriority 已废弃)
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/imageInvalid src prop, hostname is not configured
  • 看到 CLS 来自文字之后图片才加载。

实操步骤

  1. <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 步
    />
  );
}
  1. LCP 图加 preload,不是 priority Next.js 16 里 priority 已废弃,改用 preload,它会在 <head> 插一个 <link rel="preload">。一页只标一张——通常就是 hero,绝不是 footer logo。官方文档提醒:当不同视口下 LCP 元素会变时,优先用 fetchPriority="high" 而非 preload,免得预加载了某些断点下其实隐藏的图。

  2. 配好 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。这个列表保持短而明确。

  1. 远程图要配精准的 remotePatterns hostname 别用裸 **。Next.js 16 文档警告:省略 protocolportpathnamesearch 会隐式变成通配,让人能把任意图片塞进你的优化器。也可以用简写 new URL('https://example.com/account123/**')。自己 /public 下的图,用 localPatterns 锁住:
images: {
  localPatterns: [
    { pathname: '/assets/images/**', search: '' },
  ],
},
  1. sizes 决定拉哪个宽度。 不设的话,Next.js 只生成 1x/2xsrcset;设了才有完整响应式集(640w750w……),浏览器才能挑对。常见文章 hero(移动满宽、桌面约 800px)的示例:
<Image
  src={heroImg}
  alt="..."
  sizes="(max-width: 640px) 100vw,
         (max-width: 1024px) 80vw,
         800px"
  preload
/>
  1. fill 必须配定位父元素relativeabsolutefixed),否则图高度坍成 0。宽高比不固定时用它:
<div style={{ position: 'relative', aspectRatio: '16/9' }}>
  <Image src="/cover.jpg" alt="..." fill style={{ objectFit: 'cover' }} sizes="100vw" />
</div>
  1. 非 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 },
};
  1. 用 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
  1. 测 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,已优化图的存储约翻倍。配上新的 maximumDiskCacheSize LRU 淘汰,一般没事——但在小 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>

相关阅读

标签: #独立开发 #Next.js #Core Web Vitals #Technical SEO #入门