Next.js 图片优化基础

`next/image` 是你白拿的最大性能收益——前提是真配上。本文给你必须配对的四件事。

图片是内容页最重的资源,也是 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 来自文字之后图片才加载。

实操步骤

  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"
      priority    // 只有 LCP 图加 —— 见第 2 步
    />
  );
}
  1. LCP 图加 priority 关掉懒加载,加上 fetchpriority="high"。一页最多一两张图标——通常就是 hero,绝不是 footer logo。

  2. 远程图必须配 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;
  1. sizes 很关键。 不设的话浏览器会按 deviceSizes 里最大那个拉,与布局无关。常见文章 hero(移动满宽、桌面约 800px)的示例:
<Image
  src={heroImg}
  alt="..."
  sizes="(max-width: 640px) 100vw,
         (max-width: 1024px) 80vw,
         800px"
  priority
/>
  1. fill 必须配定位父元素,否则图高度坍成 0。宽高比不固定时必须用:
<div style={{ position: 'relative', aspectRatio: '16/9' }}>
  <Image src="/cover.jpg" alt="..." fill style={{ objectFit: 'cover' }} sizes="100vw" />
</div>
  1. 非 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 },
};
  1. DevTools Network 验证。hero 图应该只请求一次,content-type 是 image/aviffetchpriority: 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
  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'

容易踩的坑

  • 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,安全考虑默认禁用。

相关阅读

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