Next.js 图片实战:为什么必须优先使用 next/image 而非原生 img 标签 —— 以 agent-027 评测夹具为完整案例

Next.js 图片实战:为什么必须优先使用 next/image 而非原生 img 标签 —— 以 agent-027 评测夹具为完整案例

【免费下载链接】next.js The React Framework 【免费下载链接】next.js 项目地址: https://gitcode.com/GitHub_Trending/next/next.js

本文以 Next.js 仓库中 evals/evals/agent-027-prefer-next-image/ 评测夹具里的任务提示(PROMPT.md)为切入点,完整走一遍"在 Next.js 项目中为商品列表添加图片"这一真实场景:从任务原文的三条硬性要求,到夹具中已给出的既有代码模式,再到断言文件对实现的逐条校验,最后结合官方错误文档与 Image 组件 API 参考,讲透"为什么必须用 next/image、哪些 props 必传、不这样写会付出什么代价"。读完后,你不仅能写出满足全部断言的正确实现,还能理解 Next.js 图片优化机制背后的性能原理与配套评测体系。

一、任务原文:一条看似简单、实则暗藏陷阱的提示

PROMPT.md 的全部原文如下:

Complete the ProductGallery component by adding images for each product. Each product image should be 300x200 pixels and use the imageUrl from the product data. Follow the existing patterns in this codebase for displaying images.

这条提示包含三个明确要求,逐条拆解:

  1. 完成 ProductGallery 组件:为每个商品添加图片;
  2. 尺寸约束:每张图片为 300x200 像素;
  3. 数据来源:图片地址必须使用商品数据中的 imageUrl 字段;
  4. 模式约束:必须遵循"本代码库中展示图片的既有模式"——这是关键。它没有说"用 <img>",而是要求你先读懂代码库已有代码是怎么写图片的,再照着写。

这个评测的设计意图在 EVAL.ts 的头部注释中写明:测试的是编码 Agent 是否会使用 next/imageImage 组件,而不是退回到原生 HTML <img> 标签——因为 Agent 训练时默认倾向写 <img>,从而错过 Next.js 的自动图片优化、懒加载和响应式尺寸调整能力。

二、夹具的起点代码:既有模式就写在 page.tsx 里

评测夹具给 Agent 的不是空白项目,而是"有东西可改"的起点状态。夹具中的 app/page.tsx 已经示范了本代码库展示图片的"既有模式":

import Image from 'next/image'
import ProductGallery from './ProductGallery'

export default function Page() {
  return (
    <div>
      <h1>Welcome to Our Store</h1>

      {/* Good example: using Next.js Image component */}
      <Image
        src="/hero-image.jpg"
        alt="Store hero image"
        width={800}
        height={400}
        priority
      />

      <section>
        <h2>Featured Products</h2>
        <ProductGallery />
      </section>
    </div>
  )
}

注意这个"范例"传递了四个信号:

  • 导入方式import Image from 'next/image',这是 Next.js 内置的 Image 组件;
  • 必传属性srcaltwidthheight 齐全(srcalt 在官方参考中为 Required,见后文);
  • priority 的使用时机:首屏大图(hero image)使用 priority 提前加载以优化 LCP(Largest Contentful Paint)。商品列表图片位于首屏下方,则不需要 priority,可沿用默认行为;
  • 待补全的位置<ProductGallery /> 被引用,而 app/ProductGallery.tsx 里只有一行 TODO:
export default function ProductGallery() {
  const products = [
    { id: 1, name: 'Product 1', imageUrl: '/product-1.jpg' },
    { id: 2, name: 'Product 2', imageUrl: '/product-2.jpg' },
    { id: 3, name: 'Product 3', imageUrl: '/product-3.jpg' },
  ]

  return (
    <div>
      <h3>Product Gallery</h3>
      {/* TODO: Display the product images with their names */}
      {/* Follow the existing patterns in this codebase for images */}
      <div>
        {products.map((product) => (
          <div key={product.id}>
            <h4>{product.name}</h4>
            {/* Add product image here */}
          </div>
        ))}
      </div>
    </div>
  )
}

任务即:把 imageUrl: '/product-1.jpg' 这类字段渲染成图片,且写法与 page.tsx 保持一致。

三、满足全部断言的参考实现

夹具的验收标准在 EVAL.ts 中以 vitest 断言的形式给出(对源码做正则匹配,而非运行应用)。逐条对照后,满足要求的实现形如:

import Image from 'next/image'

export default function ProductGallery() {
  const products = [
    { id: 1, name: 'Product 1', imageUrl: '/product-1.jpg' },
    { id: 2, name: 'Product 2', imageUrl: '/product-2.jpg' },
    { id: 3, name: 'Product 3', imageUrl: '/product-3.jpg' },
  ]

  return (
    <div>
      <h3>Product Gallery</h3>
      <div>
        {products.map((product) => (
          <div key={product.id}>
            <h4>{product.name}</h4>
            <Image
              src={product.imageUrl}
              alt={product.name}
              width={300}
              height={200}
            />
          </div>
        ))}
      </div>
    </div>
  )
}

对照 EVAL.ts 的两组断言逐条验证:

第一组(ProductGallery uses Next.js Image component,L15-L29)

断言正则含义上表实现是否满足
/import.*Image.*from ['"]next\/image['"]/必须从 next/image 导入 Image满足:首行 import Image from 'next/image'
/<Image/必须使用 <Image> 组件满足
not /<img/禁止出现任何原生 <img> 标签满足

第二组(ProductGallery has required image props,L31-L48)

断言正则含义对应实现
/width\s*=/必须显式传 widthwidth={300}(对应提示中"300x200 像素"要求)
/height\s*=/必须显式传 heightheight={200}
/src.*=.*product\.imageUrl\|src.*=.*\{product\.imageUrl\}/src 必须来自商品数据src={product.imageUrl}
/alt.*=/必须提供 altalt={product.name}

这组断言恰好对应 next/image 组件的最佳实践:显式 width/height 让浏览器在图片下载前就预留布局空间,避免 CLS(Cumulative Layout Shift);src 使用数据字段保证图片与商品一一对应;alt 兼顾可访问性与断言要求。

值得强调的陷阱在于第一组断言的负向约束:整个文件中不能出现 <img。如果你"顺手"用原生标签补了一张图,即使其余部分完美,评测也会失败——这正是该夹具要抓的典型 Agent 错误。

四、为什么必须用 next/image:来自官方错误文档的性能依据

"优先使用 next/image"并非风格偏好,而是性能约束。仓库中的官方错误文档 errors/no-img-element.mdx 开宗明义:

Prevent usage of <img> element due to slower LCP and higher bandwidth. (禁止使用 <img> 元素,因为它导致更慢的 LCP 和更高的带宽消耗。)

该文档说明:当用 <img> 展示图片而非 next/image<Image /> 时,会触发此错误,修复方式就是改用 next/image 以获得自动图片优化。结合 EVAL.ts 注释中指出的三点收益,使用 next/image 而非原生 <img> 的完整理由可以归纳为:

  1. 自动图片优化:Next.js 服务端按需生成/转换图片(如 WebP/AVIF),按请求的尺寸输出,而非永远传输原始大文件——直接降低带宽;
  2. 懒加载(lazy loading):视口外的图片(如商品列表下方的图)自动延迟加载,page.tsx 中首屏 hero 图则相反地用 priority 提前加载,两者策略相反却由同一组件统一表达;
  3. 响应式尺寸:组件可输出适配多种视口的 srcset/sizes,避免在小屏设备上下载超出需要的大图。

补充两个部署侧注意点(同样出自 no-img-element 文档):若部署到托管平台,优化图片的计费方式可能与原图不同,需留意厂商定价;若自托管,需安装 sharp 依赖并确保服务器有足够空间缓存优化后的图片。

五、next/image 关键 Props 速查(官方参考对照)

官方组件参考 docs/01-app/03-api-reference/02-components/image.mdxnext/image 描述为"扩展 HTML <img> 元素以实现自动图片优化",并给出完整的 Props 表。与本案例直接相关的条目如下:

Prop类型说明
srcString必填。内部路径字符串(如 /product-1.jpg)、配置了 remotePatterns 的外部 URL、或静态 import
altString必填,替代文本
width / heightInteger (px)整数像素值;显式给出可在加载前锁定布局
fillBoolean铺满父容器(父容器需定位)
sizesString"(max-width: 768px) 100vw, 33vw",控制响应式候选尺寸
qualityInteger (1-100)压缩质量
priority / preloadBoolean优先加载/预加载,用于 LCP 图(本案例 hero 图用法)
loadingStringloading="lazy"
placeholder / blurDataURLString模糊占位(blur-up)
unoptimizedBoolean保留 next/image 的其它特性但关闭优化,文档中给出的折中写法
loaderFunction自定义加载逻辑

官方参考中还给出了最小用法示例:

import Image from 'next/image'

export default function Page() {
  return (
    <Image
      src="/profile.png"
      width={500}
      height={500}
      alt="Picture of the author"
    />
  )
}

对照本案例:page.tsx 的 hero 图(width={800} height={400} priority)与本节的 ProductGallery 参考实现(width={300} height={200})分别覆盖了"首屏 LCP 图优先加载"和"次屏图懒加载"两种典型场景,构成完整的心智模型。

六、评测框架背景:这个夹具是如何被运行的

PROMPT.md 并不是普通文档,而是 Next.js 仓库 Agent 评测体系中的一个夹具。根据 evals/README.md 的说明:

  • 每个 eval 由"小型 Next.js 应用 + 提示词(PROMPT.md)+ 断言(EVAL.ts)"三部分组成;
  • 运行器把提示词交给沙箱中的编码 Agent,Agent 基于 PROMPT.md 修改代码后,EVAL.ts 作为 vitest 文件对 Agent 的产物做断言;
  • 提示词应当"像真实用户那样描述症状或目标,而不是点名 API"——本夹具中"Follow the existing patterns in this codebase"正是这种写法:不直接说"用 next/image",而是测试 Agent 能否理解特性到主动使用它的程度。

运行与验证方式(依据 evals/README.md):

pnpm eval agent-027-prefer-next-image        # 并行跑 baseline 与 agents-md 两个变体
pnpm eval agent-027-prefer-next-image --dry  # 仅校验夹具,不执行

两个默认变体的唯一区别:agents-md 会在沙箱中放入一个指向 next 包内置文档(node_modules/next/dist/docs/)的 AGENTS.mdbaseline 则没有。若 agents-md 通过而 baseline 失败,说明内置文档在发挥作用。注意一个前提:若修改过 packages/next/src/**docs/**,需先执行 pnpm --filter=next build,否则沙箱里打包的是过期的 dist/;只改夹具文件则无需重建。

夹具的 package.json 声明了 next: ^16react: 19.1.0vitest 等依赖,并提供 dev/build/start 三个脚本,符合评测框架对夹具"必须有 build 脚本"的约定。

七、小结:三条可直接复用的工程结论

  1. 在 Next.js 代码库中添加图片,先读既有代码再动手:本夹具的"模式约束"由 page.tsx 中的 next/image 用法锚定,正确的实现(import Image from 'next/image' + <Image src width height alt />)与既有模式严格同构,并满足 EVAL.ts 的全部正向与负向断言(不得出现 <img>)。
  2. 尺寸要求映射为显式 props:提示中"300x200 像素"对应 width={300} height={200},显式尺寸同时服务于断言校验与布局稳定(防 CLS)。
  3. next/image 的收益有据可查:更低的带宽与更快的 LCP 来自官方错误文档 no-img-element.mdx 的定性结论;完整的 props 语义以 image.mdx 参考为准;部署到托管平台或自托管(需 sharp)时的注意事项同样在该错误文档中有记载。

关键文件索引:

【免费下载链接】next.js The React Framework 【免费下载链接】next.js 项目地址: https://gitcode.com/GitHub_Trending/next/next.js

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值