POST://nextjs-image-optimization
带点橘子味的馒头的头像
带点橘子味的馒头

FRONTEND / DESKTOP DEV

返回文章列表

Next.js 图片优化:从懒加载占位到性能的完整认知

1 分钟· 826 14 次阅读

图片通常是页面体积最大的资源,也是首屏性能最容易翻车的地方。这篇不谈"要用 WebP"这种老生常谈,而是讲清 浏览器 + 框架两层 对图片的处理机制,以及我们该在哪里发力。

一、浏览器的懒加载干预

现在的浏览器对 <img> 有原生懒加载:给 loading="lazy",滚动到视口附近才开始加载。但它的行为有个细节容易踩坑——

<img loading="lazy" src="big.png" />

浏览器为了省流量,会把视口外的懒加载图替换成占位符,推迟 load 事件。这在控制台会有这样的提示:

[Intervention] Images loaded lazily and replaced with placeholders.
Load events are deferred.

关键认知:这不是错误,是浏览器的主动优化。但要小心两点:

  1. 首屏内的图不要 lazy。第一屏就可见的图用 lazy 反而会延迟渲染,首屏应给 loading="eager" 或直接不给 loading(默认 eager)。
  2. useEffect 里读图片尺寸/加载状态会有偏差。懒加载图的 load 事件被推迟,实际可能比你预期的晚很多才触发。

二、next/image 到底做了什么

Next.js 的 <Image> 组件不是简单包一层 img,它做了四件框架层面的事:

import Image from "next/image";

<Image
  src="/cover.jpg"
  width={1200}
  height={630}
  sizes="(max-width: 768px) 100vw, 768px"
  priority // 首屏图加这个,等于 eager + 预加载
/>
  1. 自动生成多种尺寸 + 按视口下发合适体积的图片(sizes 告诉它选哪张)。
  2. 内置 lazy 载入机制:IntersectionObserver 检测元素进入视口才真正发请求。
  3. 保留宽高比占位:传了 width/height 就能定死盒子,避免布局抖动(CLS)。
  4. 占位方案可配placeholder="blur" 可以用极小缩略图占位,配合 blurDataURL

为什么有些图会"闪烁"?

如果你看到图片先是一小片模糊、滚动到才清晰,那是 next/image 的懒加载 + placeholder 在起作用。它不是 bug。如果你不想要这个行为:

<Image ... priority />           // 首屏:立即加载,不懒加载
<Image ... loading="eager" />    // 明文要求 eager

三、远程图片的坑

next/image 默认不允许任意远程域名(防 SSRF/性能)。要在 next.config 白名单:

// next.config.ts
export default {
  images: {
    remotePatterns: [
      { protocol: "https", hostname: "images.example.com" },
    ],
  },
};

注意:远程图无法自动知道宽高,必须手动传 widthheight(或 fill + 父容器定高),否则无法占位、会跳动。

四、最佳实践清单

场景 做法
首屏 hero 图 <Image priority />,不 lazy
文章列表封面 用 next/image,sizes 给准确值
正文内大图,不急着显示 保持默认 lazy,接受占位
远程图片 白名单域名 + 手动宽高
纯装饰图 直接 CSS background,不进 image 优化管线

小结

图片优化的本质是多尺寸下发 + 懒加载 + 占位防抖动三件事。浏览器和 next/image 已经做了大部分,我们要做的就是:首屏 eager、准确 sizes、远程图给宽高。想清楚"谁在加载、什么时候加载",就不会被各种"闪烁"和"占位"吓到——那都是优化在正常工作。

真正要警惕的反而是:把首屏图 lazy、sizes 乱填、远程图不给尺寸——这三件事会让优化反过来变成瓶颈。

相关文章