首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >WordPress 图片链路配置顺序:上传尺寸、WebP、响应式图与懒加载

WordPress 图片链路配置顺序:上传尺寸、WebP、响应式图与懒加载

原创
作者头像
用户10345225
发布于 2026-10-27 11:26:56
发布于 2026-10-27 11:26:56
230
举报

图片这条链路,出错通常不是某个开关没开,而是顺序错了:先开了懒加载再回头调尺寸,或者先转成 WebP、后来又加了一个新尺寸,结果一部分图有 WebP、一部分没有,srcset 里还挂着一堆已经不存在的文件。

下面按执行顺序走一遍,每一步给出能直接用的代码和验证方法。

一、顺序表

顺序

做什么

这一步的产物

提前或推后做的后果

1

确定要生成哪些尺寸

尺寸注册表

事后再加尺寸,全站要重生成

2

限制原图上限、设压缩质量

生成参数

已生成的缩略图仍按旧参数

3

重新生成缩略图

完整的文件集合

后面每一步都基于残缺的文件集合

4

生成 WebP 派生文件

.webp 兄弟文件

先转 WebP 再加尺寸,新尺寸没有 WebP

5

校正 srcset / sizes

正确的候选图与渲染宽度

sizes 不准,浏览器挑过大的图

6

懒加载与首图例外

加载策略

首图被懒加载,首屏先白一块

7

验证

可复核的证据

前面全对,也可能被缓存挡住

一句话记法:先决定生成什么,再决定怎么生成,最后决定怎么发出去。

二、第 1 步:确定要生成哪些尺寸

做法:打开你自己的页面,用开发者工具量三类宽度——正文内容区最大宽度、列表卡片宽度、缩略图宽度。按这三类注册,不要按「小中大特大」拍脑袋加。

代码放进子主题的 functions.php 或一个自建小插件:

代码语言:php
复制
<?php
/**
 * 只注册真正用到的尺寸。
 */
add_action( 'after_setup_theme', function () {
    add_image_size( 'hero-1600', 1600, 0, false );   // 首屏大图,不限高
    add_image_size( 'card-800', 800, 0, false );     // 列表卡片
    add_image_size( 'thumb-400', 400, 400, true );   // 正方形缩略图,裁剪
} );

/**
 * 让这几个尺寸出现在编辑器的尺寸下拉里。
 */
add_filter( 'image_size_names_choose', function ( $sizes ) {
    return array_merge( $sizes, [
        'hero-1600' => '首屏大图 1600',
        'card-800'  => '卡片 800',
        'thumb-400' => '缩略图 400',
    ] );
} );

关掉用不到的默认尺寸,减少每次上传生成的文件数量:

代码语言:php
复制
add_filter( 'intermediate_image_sizes_advanced', function ( $sizes, $metadata ) {
    unset( $sizes['medium_large'], $sizes['1536x1536'], $sizes['2048x2048'] );
    return $sizes;
}, 10, 2 );

两个坑:

  • 主题和插件各注册一套,同一张图会生成两份文件。上线前跑一次重生成,看输出里的文件数量对不对。
  • 关默认尺寸前,先确认已有文章没有引用 medium_large 这类尺寸名,否则老文章会取不到图。

三、第 2 步:原图上限与压缩质量

必须放在重生成之前——重生成用的是当时的参数,参数改了就得再跑一遍。

代码语言:php
复制
// 超过这个边长先等比缩小再存,避免把相机原图直接塞进 uploads。
add_filter( 'big_image_size_threshold', function () {
    return 2000;
} );

// JPEG 质量单独设,其他格式沿用编辑器默认。
add_filter( 'wp_editor_set_quality', function ( $quality, $mime_type ) {
    return 'image/jpeg' === $mime_type ? 82 : $quality;
}, 10, 2 );

质量值按你的图片内容调:产品图、带文字的截图调低会看出糊,实景照片 80 上下通常看不出来。改完自己放大到 100% 对比一眼再定。

四、第 3 步:重新生成

先备份数据库和 wp-content/uploads,再执行:

代码语言:bash
复制
# 只补缺失的尺寸,速度快
wp media regenerate --only-missing --yes

# 改了质量参数就要全量重生成,量大时按年月分批
wp media regenerate --yes $(wp post list --post_type=attachment --year=2026 --field=ID)

五、第 4 步:WebP 派生

先确认主机或 CDN 有没有现成的转换能力,有就别在站内再做一份:

代码语言:bash
复制
curl -sI -H 'Accept: image/webp' https://example.com/wp-content/uploads/2026/10/photo.jpg | grep -i '^content-type'

返回 image/webp 说明链路已经在转换。返回 image/jpeg 才需要自己生成「兄弟文件」,再让服务器按 Accept 协商:

代码语言:php
复制
/**
 * 为每个已生成的子尺寸同时产出一份 .webp 兄弟文件,文件名形如 photo.jpg.webp。
 * 需要 Imagick 或 GD 带 WebP 支持。
 */
add_filter( 'wp_generate_attachment_metadata', function ( $metadata, $attachment_id ) {
    if ( empty( $metadata['file'] ) ) {
        return $metadata;
    }
    if ( ! in_array( get_post_mime_type( $attachment_id ), [ 'image/jpeg', 'image/png' ], true ) ) {
        return $metadata;
    }
    if ( ! function_exists( 'imagewebp' ) ) {
        return $metadata;
    }

    $uploads  = wp_get_upload_dir();
    $base_dir = trailingslashit( $uploads['basedir'] );

    $targets = [ $metadata['file'] ];
    foreach ( $metadata['sizes'] ?? [] as $size ) {
        if ( ! empty( $size['file'] ) ) {
            $targets[] = dirname( $metadata['file'] ) . '/' . $size['file'];
        }
    }

    foreach ( array_unique( $targets ) as $relative ) {
        $src = $base_dir . $relative;
        $dst = $src . '.webp';
        if ( ! file_exists( $src ) || file_exists( $dst ) ) {
            continue;
        }
        $editor = wp_get_image_editor( $src );
        if ( is_wp_error( $editor ) ) {
            continue;
        }
        $editor->set_quality( 82 );
        $saved = $editor->save( $dst, 'image/webp' );
        if ( is_wp_error( $saved ) && file_exists( $dst ) ) {
            @unlink( $dst ); // 清掉写了一半的文件
        }
    }

    return $metadata;
}, 20, 2 );

服务器侧(map 必须放在 http 块里):

代码语言:nginx
复制
map $http_accept $webp_suffix {
    default          '';
    '~*image/webp'   '.webp';
}

location ~* ^/wp-content/uploads/.+\.(jpe?g|png)$ {
    add_header Vary Accept;
    try_files $uri$webp_suffix $uri =404;
}

三个注意点:

  • 文件名是 photo.jpg.webp 这种拼接形式,try_files 才能直接拼出来。
  • 必须发 Vary: Accept,否则 CDN 和反向代理会把 WebP 响应发给不支持的浏览器。
  • 页面缓存和 CDN 缓存要按 Accept 区分,不然第一个访问者决定了后面所有人拿到什么。

六、第 5 步:校正 srcset 和 sizes

srcset 由系统按已生成的尺寸自动输出,一般不用管。sizes 是告诉浏览器「这张图在页面上实际占多宽」,默认值往往和你的布局对不上,浏览器就会挑一张过大的图下载。

代码语言:php
复制
add_filter( 'wp_calculate_image_sizes', function ( $sizes, $size, $image_src, $image_meta, $attachment_id ) {
    $width = is_array( $size ) ? (int) $size[0] : 0;

    if ( $width >= 1200 ) {
        return '(max-width: 900px) 100vw, 900px';
    }
    if ( $width >= 600 ) {
        return '(max-width: 900px) 50vw, 400px';
    }
    return $sizes;
}, 10, 5 );

验证方法:把窗口拖到手机宽度,在开发者工具 Network 里看这张图实际下载的宽度,应该接近它在页面上的显示宽度,而不是等于原图宽度。内容区最大宽度改了,这里要同步改。

七、第 6 步:懒加载与首图例外

正文图片默认懒加载是合理的;首屏大图不行——它被推迟到布局完成之后才加载,首屏就先白一块。

模板里能显式写属性的地方,显式写:

代码语言:php
复制
echo wp_get_attachment_image( $hero_id, 'hero-1600', false, [
    'loading'       => 'eager',
    'fetchpriority' => 'high',
    'decoding'      => 'async',
    'sizes'         => '(max-width: 900px) 100vw, 900px',
    'class'         => 'hero-img',
] );

只有在改不了模板时,才用过滤器排除正文首图:

代码语言:php
复制
add_filter( 'wp_lazy_loading_enabled', function ( $default, $tag_name, $context ) {
    if ( 'img' !== $tag_name || is_admin() || is_feed() ) {
        return $default;
    }
    static $first = true;
    if ( $first ) {
        $first = false;
        return false;
    }
    return $default;
}, 10, 3 );

这里靠渲染顺序判断「第一张」,如果页头 logo 先渲染,被放过的就是 logo 而不是正文首图。能写属性就别用这段。

八、第 7 步:验证

代码语言:bash
复制
# WebP 协商是否生效
curl -sI -H 'Accept: image/webp' https://example.com/wp-content/uploads/2026/10/photo.jpg | grep -i '^content-type'

# 文件是否真的存在
curl -sI https://example.com/wp-content/uploads/2026/10/photo.jpg.webp | head -n 1

# 页面里的 img 标签
curl -s https://example.com/sample-page/ | grep -o '<img[^>]*>' | head -n 5

再加两个肉眼检查:首图是不是 eager 加 fetchpriority=high;把网络限速到慢速 3G,看首屏是否先出图。

顺序错了的典型现象:

  • 改了尺寸但页面没变——页面缓存或 CDN 缓存没清。
  • 首图有 WebP 但卡片图没有——WebP 生成放在了加尺寸之前。
  • 手机上下的图比电脑还大——sizes 没跟着布局改。

九、什么情况不值得自己搭这套

  • 图片量很大(商品图、图库):交给对象存储或 CDN 的图片处理,站内不要再生成一份,否则一份图存两套格式还两处不同步。
  • 全站就十几张图:上传前手工导出 WebP 直接传,省掉整条链路。
  • 主题自带完整的图片处理:先确认它注册了哪些尺寸、有没有转 WebP,别两边叠加。

改完想整体过一遍线上表现,可以用站点检查工具跑一次:https://aumcreate.com/tools/site-check?utm_source=tencent&utm_medium=content&utm_campaign=tencent-image-webp-lazy-order

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

目录
  • 一、顺序表
  • 二、第 1 步:确定要生成哪些尺寸
  • 三、第 2 步:原图上限与压缩质量
  • 四、第 3 步:重新生成
  • 五、第 4 步:WebP 派生
  • 六、第 5 步:校正 srcset 和 sizes
  • 七、第 6 步:懒加载与首图例外
  • 八、第 7 步:验证
  • 九、什么情况不值得自己搭这套
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档