Next.js 15/16 next/image 完全指南:remotePatterns、AVIF/WebP 与 LCP 优化实战 (2026)

next/image 是 Next.js 内置的图片组件,自动完成尺寸缩放、AVIF/WebP 格式转换、响应式 srcset 与懒加载。本文覆盖 Next.js 15 与 16 的关键 API、remotePatterns 白名单、LCP 优化以及 Vercel 计费踩坑。

Next.js 15/16 next/image 完全指南 (2026)

更新于:2026 年 8 月 13 日

next/image 是 Next.js 内置的图片组件,会在请求时自动完成尺寸缩放、格式转换(AVIF/WebP)、按设备分发响应式 srcset、懒加载,以及在 CDN 上缓存转换结果。用它替换原生 <img>,配合 remotePatternspriority(Next.js 16 起改名为 preload)、sizesblurDataURL 四项配置,通常能把一张 1.2 MB 的 JPEG hero 缩到 45 KB 左右,把 LCP 从 4 秒以上拉进 1.5 秒内。本文覆盖 Next.js 15 与即将成为主线的 16 的所有关键 API 和踩坑点。

  • next/image 通过 /_next/image 路由按需转换:resize 加格式协商(AVIF/WebP)再加 CDN 缓存,首次请求耗算力,之后走缓存。
  • 必须在 next.config.ts 中用 remotePatterns 白名单外部域名,旧的 images.domains 已弃用;未配置会抛 hostname is not configured
  • LCP 图片必须显式加 priority(Next.js 16 起使用 preload)。它会插入 <link rel="preload"> 并设置 fetchpriority="high"
  • sizes 是响应式图片的关键:漏掉这一项时浏览器会按最大 deviceSizes 下载,移动端会白白拉大图。
  • 本地静态导入的 src 会自动生成 blurDataURL;远端图片必须自己提供,建议用 Plaiceholder 之类的构建期工具生成 ≤ 100 字节的 base64。
  • Vercel Image Optimization 按 源图 计费(Source Images),自建时用 Sharp 即可;关闭平台优化改走 Cloudinary/imgix 的 loader 也是常见方案。

next/image 到底做了什么

说实话,许多人第一次用 next/image 时以为它只是把 <img> 替换成一个「更聪明的」组件。事实上它背后是一条完整的请求期图像优化管线。当浏览器请求一张 next/image 输出的图片时,实际命中的是 Next.js 内置的 /_next/image 端点,这个端点会做四件事:

  1. 根据 URL query 里的 w(宽度)参数把原图 resize 到目标像素。目标宽度来自 deviceSizes / imageSizes 生成的候选集。
  2. 读取浏览器发来的 Accept 头,如果包含 image/avif 就返回 AVIF,其次是 WebP,都不支持才回退到原格式。
  3. Sharp(libvips 绑定)压缩,quality 默认 75。
  4. 把结果写入磁盘(自建)或边缘节点缓存(Vercel),后续同参数请求直接命中缓存。

换句话说,首次请求某个尺寸变体的用户要为一次转换付费:CPU 时间、Sharp 依赖,可能还有冷启动。之后所有人都走 CDN 缓存。理解这一点后你会发现两件事。其一,源图越大越糟,4000 px 的 PNG 扔进 public/ 不是「零成本」,只是把成本推迟到第一次访问。其二,deviceSizes 数组每多一个断点,都会为每张图多引入一次冷缓存的可能性。

Next.js 15.4 起,next/image 默认已启用 image/avif 优先级,同时保留 image/webp fallback。Next.js 16 主要变化是把 priority 属性重命名为 preload,语义没变,但更贴合它做的事(插入 <link rel="preload">)。

最小可用示例与 width/height 的规则

最简单的用法是导入一张本地图片。因为是静态导入,Next.js 在构建期就能读到宽高和 blurDataURL,所以不用手写:

// app/page.tsx
import Image from 'next/image';
import hero from '@/public/hero.jpg';

export default function Page() {
  return (
    <Image
      src={hero}
      alt="产品概览"
      priority          {/* Next.js 16 请改成 preload */}
      placeholder="blur"
      sizes="100vw"
    />
  );
}

如果 src 是字符串(远端图片或 /public 下的路径),你必须同时提供 widthheight,或使用 fill。这是为了让 Next.js 在图片加载前就能在布局里预留正确的宽高比,从而避免 CLS(累积布局偏移)。

// 远端图片必须显式指定宽高
<Image
  src="https://images.example.com/banner.jpg"
  alt="活动 banner"
  width={1600}
  height={800}
  sizes="(max-width: 768px) 100vw, 1600px"
/>

// 或者用 fill 让图片撑满父容器(父容器必须 position: relative 且有明确尺寸)
<div className="relative aspect-[2/1] w-full">
  <Image src="/gallery/1.jpg" alt="" fill sizes="100vw" />
</div>

配置 remotePatterns:外部图片的正确姿势

如果你从 Cloudinary、S3、Sanity、Contentful 之类的服务加载图片,必须在 next.config.ts 里显式白名单,否则会直接抛出 hostname "xxx" is not configured under images in your next.config.js

官方推荐的写法是 remotePatterns。旧的 images.domains 数组从 Next.js 15.3 开始被标记 deprecated,Next.js 16 会移除。remotePatterns 允许在 protocolhostnameportpathnamesearch 五个维度做精确匹配,安全性显著更高。毕竟 /_next/image 本质上是一个转发代理,白名单太宽等于把带宽和 CPU 免费送给互联网。

// next.config.ts
import type { NextConfig } from 'next';

const config: NextConfig = {
  images: {
    formats: ['image/avif', 'image/webp'],
    deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048],
    imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
    minimumCacheTTL: 60 * 60 * 24 * 30, // 30 天
    remotePatterns: [
      {
        protocol: 'https',
        hostname: 'images.unsplash.com',
        pathname: '/**',
      },
      {
        protocol: 'https',
        hostname: '**.cloudfront.net',
        pathname: '/uploads/**',   // 只允许 uploads 目录,避免代理任意路径
      },
      {
        protocol: 'https',
        hostname: 'cdn.sanity.io',
        pathname: '/images/PROJECT_ID/**',
      },
    ],
  },
};

export default config;

AVIF 还是 WebP?格式协商与体积对比

默认情况下 formats: ['image/webp'],只输出 WebP。手动加上 AVIF 后,Next.js 会按顺序尝试:「浏览器支持 AVIF?给 AVIF;否则 WebP;否则原图」。Can I Use 的 2026 年数据显示 AVIF 在全球移动端的支持率已超过 96%,主流场景下几乎可以按开就开。

维度WebPAVIF
相同视觉质量下体积(vs JPEG)小 25–34%小 40–55%
编码 CPU 成本(相对)4–8×
2026 浏览器覆盖率> 98%> 96%
动画支持支持支持
推荐场景自建、CPU 受限Vercel、静态站

如果你部署在 Vercel,Image Optimization 是托管服务,AVIF 的额外编码成本对你不可见,直接开就行。如果自建(Node/Docker),流量峰值时 AVIF 转换可能把 CPU 打满,可以只用 WebP,或者引入独立的转换 worker 池。

如何用 priority/preload 优化 LCP

LCP(Largest Contentful Paint)是 Core Web Vitals 里对 SEO 影响最直接的指标,而首屏 hero 图往往就是 LCP 元素。next/image 默认是 lazy 的:它会等到图片进入视口才发起请求,这在下方内容里节省流量,但对首屏图是灾难。浏览器要先解析 HTML、构建 CSSOM、执行部分 JS,然后才知道要下载 hero。

给首屏图加 priority(Next.js 16 起改名为 preload)会做两件事:

  • <head> 里插入 <link rel="preload" as="image">,让浏览器在解析 HTML 时就并行下载
  • <img> 加上 fetchpriority="high",提高在同一批下载中的优先级。
// app/(marketing)/page.tsx
export default function Home() {
  return (
    <section>
      <Image
        src={hero}
        alt="首页 hero"
        priority                    {/* Next.js 15 */}
        // preload                  {/* Next.js 16 */}
        sizes="100vw"
        className="w-full h-auto"
      />
      {/* 下方的图不要加 priority,让它们保持 lazy */}
      <Image src={feature1} alt="特性 1" sizes="(max-width: 768px) 100vw, 33vw" />
    </section>
  );
}

一个来自 DebugBear 的真实案例把一张配错域名的 hero 图修好、加上 priority 后,LCP 从 4.1 s 降到 2.3 s,单一变更打进了 Google 的 "Good" 区间。我自己在一个 SaaS 落地页上也复现过类似结果,不过反过来的坑同样常见:给整页所有图片都加 priority,浏览器提示 "Image was detected as the Largest Contentful Paint (LCP). Please add the 'priority' property",你以为多多益善,结果 4 张图抢带宽反而拉长 LCP。规则:每个视口只有一张 LCP,理论上只需要一个 priority。如果不同断点下 LCP 元素不同(例如移动端换成 banner),可以标多张,其余保持 lazy。

sizes 与 srcset:不要让手机下载 2048 宽图

这一节是本文最容易被跳过、也最能立竿见影拉高 Lighthouse 分数的部分。next/image 会根据 deviceSizes 生成一套 srcset,例如 640w, 750w, 828w, 1080w, 1200w, 1920w, 2048w。但浏览器怎么知道该挑哪一档?答案是 sizes 属性。你必须告诉浏览器这张图在不同断点下会以多宽渲染

没有 sizes 时浏览器会假设图片占满视口,直接下最大的那档(通常是 2048w)。对一张只在移动端渲染 375 px、桌面渲染 400 px 的头像来说,这意味着白白下载了近 25× 的像素。

// 三列网格里的卡片图
<Image
  src={cover}
  alt=""
  width={800}
  height={450}
  sizes="(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 33vw"
/>

sizes 的思路是「由小到大」:从最窄断点开始,一路描述到最宽断点。浏览器会取第一个匹配的媒体查询。上面的例子表示:视口 ≤ 640 px 时图渲染满宽;≤ 1024 px 时占一半;再宽就占三分之一。浏览器结合 devicePixelRatio 挑最合适的 srcset 档位。

如果你在做一个和 Partial Prerendering 结合的营销页,绝大多数图片是 static shell 的一部分,正确的 sizes 能直接降低静态 CDN 上冷缓存的种类,也降低 Turbopack 生产构建后首屏抓取的字节数。

placeholder="blur" 与 blurDataURL 生成策略

placeholder="blur" 会在图片解码前显示一张模糊占位图,视觉上把「空到图」的突兀切换换成「模糊到清晰」的渐现,能显著改善感知性能。它默认是 empty,改成 blur 时必须搭配 blurDataURL,一个极小的 base64 数据 URL。

本地静态导入时 Next.js 会在构建期用 Sharp 生成一张 8 px 宽的极模糊 PNG,塞进产物里,你什么都不用做。远端图片就得自己提供:

// 方案 A: 构建期用 plaiceholder 生成
// npm i plaiceholder sharp
import { getPlaiceholder } from 'plaiceholder';

async function getBlur(src: string) {
  const buffer = await fetch(src).then(r => r.arrayBuffer());
  const { base64 } = await getPlaiceholder(Buffer.from(buffer));
  return base64; // "data:image/jpeg;base64,/9j/4AAQSk..."
}

// 在 Server Component 里
export default async function ProductCard({ product }: { product: Product }) {
  const blurDataURL = await getBlur(product.imageUrl);
  return (
    <Image
      src={product.imageUrl}
      alt={product.name}
      width={600}
      height={400}
      placeholder="blur"
      blurDataURL={blurDataURL}
    />
  );
}

另一个反直觉的建议:不要给 LCP hero 用 blur 占位。你希望首屏图片直接清晰渲染,而不是先模糊一下。模糊帧会推迟 LCP 的判定时间。priority hero 图跳过 placeholder,其他图开 placeholder="blur",这是主流最佳实践。

外部 CDN loader:Cloudinary、imgix、自建

Next.js 允许你完全绕开内置的 /_next/image,改用外部 CDN 做转换。适用场景有三种:一是你已经买了 Cloudinary / imgix;二是自建 Node 下想避开 Sharp 依赖;三是走 Cloudflare Images 之类按次计价的服务。

// next.config.ts:全局 loader
const config: NextConfig = {
  images: {
    loader: 'custom',
    loaderFile: './lib/cloudflare-loader.ts',
  },
};
export default config;
// lib/cloudflare-loader.ts
import type { ImageLoaderProps } from 'next/image';

const CF_ACCOUNT = 'https://imagedelivery.net/YOUR_ACCOUNT_HASH';

export default function cloudflareLoader({ src, width, quality }: ImageLoaderProps) {
  const params = [`width=${width}`, `quality=${quality ?? 75}`, 'format=auto'];
  // src 只需要是 Cloudflare Images 的 image ID
  return `${CF_ACCOUNT}/${src}/${params.join(',')}`;
}

切成外部 loader 后,remotePatterns 不再对该图生效(因为请求根本不过 /_next/image),你需要在 CDN 侧自己配安全策略。同时也要注意:外部 loader 意味着 next.config.ts 里的 formatsdeviceSizesquality 都被绕过,所有决策由 URL 参数控制。

缓存策略、Vercel 计费与自建成本

minimumCacheTTL 控制转换结果在服务端(或边缘)的最小缓存时长,默认 60 秒。把它调大是几乎无副作用的成本优化:只要源图 URL 稳定,30 天甚至 1 年都合理。缓存 key 由源 URL、wqAccept 组成,源图变更请改文件名(或 URL query)来 bust。

Vercel 的 Image Optimization 从 2025 年起改为按 Source Images 计费,每 1000 张唯一源图 5 美元起(Hobby 免费 1000 张)。这里的坑是「唯一源图」的定义包含所有 src 变体:如果你的 CMS 每次编辑重新签名 URL,或者你把 ?v=timestamp 拼进去做 cache-bust,唯一数会爆炸。合理做法是版本号写进路径/images/v3/hero.jpg),而不是 query。我之前有个客户站点,因为签名 URL 每 15 分钟轮换,一个月账单被推到四位数,改成路径版本号后立刻回落。

自建部署(Node、Docker、Kubernetes)时 Sharp 是 next/image 的必需依赖。生产环境请显式 npm i sharp,Next.js 会 warn "Server did not have sharp installed",此时会退化到较慢的 Squoosh WASM 编码器。如果你的镜像是 node:20-alpine,Sharp 会拉预编译的 musl 二进制,无需 apk add

常见报错与排错清单

Error: hostname "xxx" is not configured

没在 remotePatterns 里加白名单。检查 next.config.ts,重启开发服务器(next dev 修改 config 后必须重启)。

upstream image response failed for /path/to.jpg 403

源站拒绝了 /_next/image 的请求。常见原因是源站有 hotlink 保护或需要 signed URL,先在浏览器 curl -I 目标图片确认 HTTP 状态。

"Image was detected as the LCP element. Consider adding the 'priority' property"

控制台警告,含义是它检测到该图是 LCP 但没标 priority。加上就消失;如果你确认它不是 LCP(例如它只是 above-the-fold 但被 hero video 覆盖),忽略即可。

图片加载时布局抖动(CLS)

90% 是没写 width/height,或者用了 fill 但父容器没有明确尺寸。参考本文最小示例里的两种正确用法。想更系统地理解水合与首屏渲染的关系,可以看我们之前写的 Next.js Suspense 与 Streaming SSR 完全指南,LCP 优化和流式渲染是一对姊妹话题。

常见问题

next/image 和原生 img 有什么区别?

原生 <img> 只是把源文件按原样返回给浏览器;next/image 会在 /_next/image 端点上按请求参数做 resize、格式转换(AVIF/WebP)、srcset 生成、懒加载与缓存,配合正确的 sizespriority 通常能节省 60–90% 的图片字节。

为什么 next/image 会报 hostname is not configured?

因为外部域名默认不允许被 /_next/image 代理。把域名加进 next.config.tsimages.remotePatterns(含 protocolhostnamepathname),然后重启 next dev

Next.js 16 里 priority 是不是被移除了?

没有移除,而是重命名为 preload。行为完全相同:插入 <link rel="preload" as="image"> 并给 <img>fetchpriority="high"priority 会作为别名保留一到两个版本,控制台给出 deprecation 提示。

如何为远端图片生成 blurDataURL?

常用方案是构建期用 plaiceholder 或 Sharp 自己 resize 到 8–16 px 后 base64 化,控制体积在 100 字节以内。运行时生成也可以,但要缓存结果,避免每次请求都跑一次 Sharp。

Vercel Image Optimization 的费用怎么算,怎么避免超支?

按「唯一源图」计费,同一 src 的所有尺寸变体只算一张源图。控制费用的核心是让 src URL 稳定:把版本号写进路径而不是 query、避免每次编辑重签 URL、必要时用外部 CDN loader 完全绕过 /_next/image

Editorial Team
关于作者 Editorial Team

Our team of expert writers and editors.