# 全站 img 统一加了懒加载，Lighthouse 跑分 LCP 反倒更慢，首屏 banner 是不是不该碰？

**URL:** <https://www.shiqidu.com/t/topic/1833>\
**Category:** 开发调优\
**Tags:** 前端优化, 前端开发, html\
**Created:** [2026 年10 月 28 日 12:47 UTC](https://www.shiqidu.com/t/topic/1833 "2026-10-28T12:47:08Z")\
**Posts on this page:** 1\
**Page:** 1

<div class="post-metadata">

作者： ![shiqidu\_editor](https://www.shiqidu.com/user_avatar/www.shiqidu.com/shiqidu_editor/32/528_2.png) [@shiqidu\_editor](https://www.shiqidu.com/u/shiqidu_editor)\
发布日期： [2026 年10 月 28 日 12:47 UTC](https://www.shiqidu.com/t/topic/1833/1 "2026-10-28T12:47:08Z")

</div>

原生懒加载只要一个 `loading="lazy"` 属性，不必引库，也不用自己监听滚动。于是不少团队干脆在封装的 `<Image>` 组件里把它设成默认，或者让构建插件给每个 `<img>` 自动补上。下面的商品列表确实省了流量，可首屏那张大 banner 也跟着被懒加载了。这时候再跑 Lighthouse，LCP 往往比改之前还难看。

## 慢在哪一步

浏览器拿到 HTML 后，预加载扫描器会抢先找出文档里的图片地址发请求，这时候页面还没开始排版。一旦图片带上 `loading="lazy"`，浏览器就得等布局算完、确认它离视口足够近，才真正发起请求。[web.dev 那篇讲优化 LCP 的文章](https://web.dev/articles/optimize-lcp)专门拿这种写法当反例。

那篇文章把 LCP 拆成四段，DevTools 和 Lighthouse 里都能看到：

- TTFB 指从发起导航到收到 HTML 第一个字节的时间。
- 资源加载延迟指 TTFB 之后到 LCP 图开始下载的这段空档。
- 资源加载耗时就是下载本身花掉的时间。
- 元素渲染延迟指下载完成后到元素真正画出来的间隔。

误用 lazy 拖长的是第二段。图片体积和网络都没变，多出来的时间全耗在迟迟不开始下载上。按 web.dev 给的参考比例，资源加载延迟和元素渲染延迟这两段，各自都该压在 LCP 的 10% 以内。LCP 想进 good 区间，得让至少 75% 的访问在 2.5 秒内完成。

用 JS 库懒加载、把地址藏进 `data-src` 的老写法也是同一个毛病，扫描器根本看不到真实的 src。至于拿 scroll 事件加防抖自己实现懒加载，那是另一类问题，这里不展开。

## 怎么改

思路是首屏和非首屏分开处理：

```html
<!-- 首屏 banner：不带 loading，提高优先级，宽高写死防 CLS -->
<img src="/img/banner-1600.webp"
     width="1600" height="600"
     fetchpriority="high"
     alt="秋季活动">

<!-- 首屏以下的商品图：照常懒加载 -->
<img src="/img/item-01.webp" loading="lazy"
     width="400" height="400" alt="商品 1">

```

改的时候还有几处要注意：

- 首屏 LCP 图去掉 `loading="lazy"`，浏览器默认就是 eager。工具链要是会自动补属性，就显式声明 `loading="eager"`，免得被改回去。
- `fetchpriority="high"` 只留给一两张最可能成为 LCP 的图。web.dev 提醒过，给太多图片设高优先级，这个提示就失去意义了。
- `fetchpriority="high"` 跟 `loading="lazy"` 叠在一起意义不大：图在屏幕外时照样被推迟，靠近视口后才以高优先级下载。
- banner 如果是 CSS 背景图，扫描器发现不了它，得在 `<head>` 里加一条 preload，这个标签本身同样要带 `fetchpriority="high"`。
- 轮播里一开始看不见的几张，可以设 `fetchpriority="low"`，把带宽让给第一张。
- 每张图都写上 `width` 和 `height`。懒加载图没有尺寸时按 0×0 算，浏览器可能认为一整排都在视口内，结果一次性全拉下来。尺寸缺失还会让晚到的图片把内容顶开，CLS 跟着变差。

背景图的 preload 大概长这样：

```html
<link rel="preload" as="image" href="/img/banner-1600.webp" fetchpriority="high">

```

属性取值可以对照 [MDN 的 img 页面](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/img)，懒加载的行为细节在 [web.dev 的 Browser-level image lazy loading](https://web.dev/articles/browser-level-image-lazy-loading) 里讲得比较全。

## 哪张才是 LCP

改之前得确认 LCP 元素是谁，有时候它是一段标题文字，根本不是那张图。

- Chrome DevTools 的 Performance 面板打开就有 live metrics，会显示本地测到的 LCP 和对应节点。录一段 trace 后，Insights 里的 LCP breakdown 会列出四段各占多少。
- Lighthouse 13 删掉了旧的 `lcp-lazy-loaded` 和 `prioritize-lcp-image` 审计。取代它们的 insight 叫 [LCP request discovery](https://developer.chrome.com/docs/performance/insights/lcp-discovery)。JSON 报告里对应的 id 是 `lcp-discovery-insight`。它检查三件事：图能不能从 HTML 里直接发现、有没有被懒加载、带没带 `fetchpriority=high`。
- 提醒推迟屏外图片的 `offscreen-images` 审计也在 13 版移除了。官方给的理由是浏览器本来就会降低屏外图片的优先级，懒加载能省带宽，对 Lighthouse 测的指标影响却不大。

控制台里也能直接打出来：

```js
new PerformanceObserver((list) => {
  const entries = list.getEntries();
  const last = entries[entries.length - 1];
  console.log(last.element, last.url, last.startTime);
}).observe({ type: 'largest-contentful-paint', buffered: true });

```

最后一条记录就是当前的 LCP 候选，`url` 为空的话，多半是文字。

你们的图片组件默认走哪边：全量 lazy 再把首屏排除掉，还是反过来只在列表里开启？
