搜索全站
主题 10 分钟阅读

WordPress 图片懒加载与 width/height 属性:根治 CLS 的做法

图片一加载页面就往下跳?本文讲清 CLS 的成因,给出 WordPress 5.5+ 懒加载机制说明、width/height 属性补齐代码、srcset 与 fetchpriority 配置,以及首图豁免懒加载的正确写法。

U
UFO_PF 2023-06-06 · 阅读 10 分钟
Wo 主题
阅读提示
本文约 3,985 字,预计阅读 10 分钟。文中命令与配置均经过实测,可直接参考执行。

引言:正文读到一半,图片加载完把文字顶走了

手机上读一篇长文,滚动到第三屏时图片才开始请求,加载完成瞬间整段文字向下跳了一截,手指正好点错位置。这个现象就是 CLS(Cumulative Layout Shift,累计布局偏移)。Google 把 CLS 列为三大核心网页指标之一,良好阈值是 0.1,超过 0.25 属于「差」,不仅体验糟糕,也会间接影响搜索排名。

CLS 的头号元凶几乎总是图片:缺少 width 和 height 属性时,浏览器在图片下载完成前不知道该给它留多高的盒子,只能先按 0 高度渲染,等图片到达再撑开,于是下方内容整体位移。WordPress 从 5.5 起已经默认给内容里的图片加 loading="lazy" 并尝试补全尺寸,但主题自定义的图片输出、the_post_thumbnail() 的某些用法、以及手工粘贴进来的 <img> 标签,仍然会漏掉这些属性。本文从机制讲到补齐代码。图片尺寸问题常常和 主题广告位与 CLS 布局偏移控制 一起出现,两者要通盘处理。

原理分析:CLS 是怎么算出来的,宽高属性为什么能救场

CLS = 影响分数(impact fraction)× 距离分数(distance fraction)。影响分数是发生位移的元素占视口的比例,距离分数是它移动的距离占视口的比例。一次 0.05 的小位移看似无害,滚动过程中累积十次就能突破 0.1。

浏览器有个机制叫「宽高比推断」:只要 <img> 带了 width 和 height 属性,即使图片还没下载,浏览器也能用这两个数字算出宽高比,配合 CSS 的 height: auto 提前预留出正确高度的盒子。图片到达后直接填满,不产生任何位移。这就是「加两个属性就能解决 CLS」的原理。

WordPress 5.5 引入的 wp_filter_content_tags() 会遍历 the_content 里的 <img>,自动补 loading="lazy"、decoding="async" 以及缺失的尺寸。但它有几个边界:只能处理保存到数据库的内容里的标签,主题模板里硬编码的 <img> 不走这个流程;wp_get_attachment_image() 输出的属性可以被 wp_get_attachment_image_attributes 过滤;阈值方面,5.9 起提供 wp_omit_loading_attr_threshold 过滤器,默认豁免第一张图不做懒加载。

属性/机制 作用 引入版本
loading="lazy" 延迟加载视口外图片 WP 5.5 默认
wp_omit_loading_attr_threshold 前 N 张图不做懒加载 WP 5.9
wp_lazy_loading_enabled 按标签与上下文开关懒加载 WP 5.5
wp_img_tag_add_width_and_height_attr 控制是否补全尺寸属性 WP 5.5
fetchpriority="high" 提升首图加载优先级 浏览器特性,非 WP 版本

保姆级实操

  1. 先量现状:装 web-vitals 或用 PageSpeed Insights 跑一次,记录当前 CLS 数值与「位移最大的元素」。
  2. 检查主题输出的 img:打开文章页源码,逐个看 <img> 是否带 width / height;没有的多半来自主题模板硬编码或旧内容。
  3. 统一补齐属性:挂 wp_get_attachment_image_attributes,从附件元数据里取尺寸,同时补 decoding="async"。
  4. 首图豁免懒加载:用 wp_omit_loading_attr_threshold 把阈值调到 2,并给首图加 fetchpriority="high"。
  5. 处理历史内容:对老文章批量跑一次正则补尺寸,或用 the_content 过滤器实时兜底。
  6. CSS 兜底:给内容区图片加 height: auto,并给容器设 aspect-ratio。
<?php
// 子主题 functions.php:统一补齐 width / height / decoding
add_filter( 'wp_get_attachment_image_attributes', 'mimangfei_ensure_image_dims', 10, 3 );
function mimangfei_ensure_image_dims( $attr, $attachment, $size ) {
    if ( empty( $attr['width'] ) || empty( $attr['height'] ) ) {
        $meta = wp_get_attachment_metadata( $attachment->ID );
        if ( ! empty( $meta['width'] ) && ! empty( $meta['height'] ) ) {
            $attr['width']  = (int) $meta['width'];
            $attr['height'] = (int) $meta['height'];
        }
    }

    $attr['decoding'] = 'async';
    return $attr;
}
<?php
// 前两张图不做懒加载,首图提升优先级(避免 LCP 被延后)
add_filter( 'wp_omit_loading_attr_threshold', function( $threshold ) {
    return 2;
} );

add_filter( 'wp_get_attachment_image_attributes', function( $attr, $attachment, $size ) {
    if ( 'mimangfei-hero' === $size ) {
        $attr['loading']       = 'eager';
        $attr['fetchpriority'] = 'high';
    }
    return $attr;
}, 20, 3 );
/* 内容区图片:保留宽高比,配合 width/height 属性预留空间 */
.entry-content img {
    max-width: 100%;
    height: auto;
    display: block;
}

/* 容器级兜底:即便图片属性缺失,也按 16:9 预留 */
.entry-content figure.wp-block-image {
    aspect-ratio: 16 / 9;
    overflow: hidden;
}

常见踩坑

  1. 只加 loading 不加宽高:懒加载解决的是带宽,解决不了 CLS。两个属性必须同时存在才有效果。
  2. CSS 里写死 height: 300px:覆盖了 height: auto,宽高比推断失效,图片会被拉伸变形。用 aspect-ratio 代替固定高度。
  3. 首图被懒加载拖慢 LCP:默认只豁免第一张,但很多主题的「相关文章缩略图」排在内容前,实际首图成了第二张。要么调阈值,要么对 LCP 图单独加 loading="eager"。
  4. 案例:某站 CLS 报 0.32,排查发现文章里所有图片都来自 Markdown 转 HTML 的历史内容,<img> 只有 src 没有尺寸。批量跑了一次 wp_filter_content_tags() 的等价逻辑(对旧内容重新保存一次即可触发),CLS 降到 0.04。教训是:早期导入的内容不会自动享受新版本的过滤能力,需要重新保存或直接改库。
  5. srcset 里尺寸不匹配:sizes 属性写得过宽,浏览器选了大图,加载慢且占位计算错误。按实际断点写 sizes,例如 (max-width: 768px) 100vw, 720px。
  6. 延迟加载与 JS 轮播冲突:轮播图的非首帧图片如果被懒加载,切换时会空白。轮播容器内的图片要显式关掉懒加载,这部分可结合 主题轮播(Slider)组件的性能代价 一起处理。
  7. attachment_url_to_postid() 在 CDN 域名下取不到 ID:图片走 CDN 后 URL 变了,按 URL 反查附件会失败。补齐尺寸时优先走附件 ID,不要靠 URL 反查。

总结

判断标准很简单:页面上每一个 <img>,都应该同时具备 width、height,并且 CSS 里有 height: auto。满足这条,图片类 CLS 归零;首图再单独加 loading="eager" 和 fetchpriority="high" 保 LCP。行动清单:跑一次 PageSpeed 拿基线 → 挂 wp_get_attachment_image_attributes 补属性 → 调 wp_omit_loading_attr_threshold → 历史内容重新保存或批量修正 → CSS 加 aspect-ratio 兜底 → 复测 CLS。做完这套,绝大多数内容站的 CLS 都能压到 0.05 以内。

U
UFO_PF作者

专注 WordPress 生态的实战作者,内容以「可复制、可验证」为第一原则。

留下你的想法

邮箱地址不会被公开。带 * 的为必填项。