You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为自定义Gutenberg区块中的图片添加srcset属性?

自定义Gutenberg区块图片srcset属性缺失问题解决方案

问题描述

我正在开发自定义Gutenberg区块,用于上传图片和编辑内容,但上传的图片无法生成srcset属性。查资料说WordPress会自动添加srcset,但我用现有代码开发时,内容保存正常,图片却没有srcset,试过很多方法都没解决。

现有代码

block.json

{
    "$schema": "https://schemas.wp.org/trunk/block.json",
    "apiVersion": 3,
    "name": "dt-panphagia/dt-panphagia",
    "version": "0.1.0",
    "title": "Panphagia",
    "category": "widgets",
    "icon": "admin-site-alt",
    "description": "",
    "keywords": [ "dt", "saurus", "archive" ],
    "supports": {
        "align": ["full", "wide"]
    },
    "textdomain": "dt-panphagia",
    "editorScript": "file:./index.js",
    "editorStyle": "file:./index.css",
    "style": "file:./style-index.css",
    "viewScript": "file:./view.js",
    "attributes": {
        "content": {
            "type": "string",
            "selector": "p"
        },
        "heading": {
            "type": "string"
        },
        "mediaId": {
            "type": "number",
            "default": 0
        },
        "mediaUrl": {
            "type": "string",
            "default": ""
        }
    }
}

edit.js

import { __ } from '@wordpress/i18n';

import { 
    useBlockProps, RichText, MediaUpload, MediaUploadCheck, InspectorControls
} from '@wordpress/block-editor';

import {
    TextControl, ToggleControl, PanelBody, PanelRow, Button, ResponsiveWrapper,
    __experimentalHStack as HStack
} from '@wordpress/components';

import { 
    Fragment 
} from '@wordpress/element'

import './editor.scss';

export default function Edit({ attributes, setAttributes, className }) {

    const blockProps = useBlockProps();

    const removeMedia = () => {
        setAttributes({
            media: ""
        });
    }

    return (
        <Fragment>
            <InspectorControls>
                <PanelBody title={__('Imagem/Achievement', 'dt-panphagia')} initialOpen={ true }>
                    <div className="editor-post-featured-image">
                        <div className='editor-post-featured-image__container'>
                        <MediaUploadCheck>
                            <MediaUpload
                                onSelect = {(media) => {
                                    setAttributes({
                                        mediaId: media['id'],
                                        mediaUrl: media['url'],
                                        mediaWidth: media['width'],
                                        mediaHeight: media['height']
                                    });
                                }}
                                type = "image"
                                value = {attributes.mediaId}
                                render = {({ open }) => {
                                    return (
                                        <Button
                                            className = {attributes.mediaId == 0 ? 'editor-post-featured-image__toggle' : 'editor-post-featured-image__preview'}
                                            onClick = {open}
                                        >
                                        {attributes.mediaId == 0 && __('Escolha uma Imagem', 'dt-panphagia')}
                                        {attributes.mediaId != 0 &&
                                                <ResponsiveWrapper naturalWidth={ attributes.mediaWidth } naturalHeight={ attributes.mediaHeight }>
                                                    <img src={attributes.mediaUrl} />
                                                </ResponsiveWrapper>
                                        }
                                        </Button>
                                    );
                                }}
                            />
                        </MediaUploadCheck>
                        {attributes.mediaId != 0 &&
                        <HStack className='editor-post-featured-image__actions'>
                            <Button className='editor-post-featured-image__action' onClick={removeMedia}>{__('Remove', 'dt-panphagia')}</Button>
                            <MediaUploadCheck>
                                <MediaUpload
                                    title={__('Replace image', 'awp')}
                                    value={attributes.mediaId}
                                    onSelect = {(media) => {
                                        setAttributes({
                                            mediaId: media['id'],
                                            mediaUrl: media['url'],
                                            mediaWidth: media['width'],
                                            mediaHeight: media['height']
                                        });
                                        {console.log(media);}
                                    }}
                                    type = "image"
                                    render = {({open}) => (
                                        <Button className='editor-post-featured-image__action' onClick={open}>{__('Replace', 'dt-panphagia')}</Button>
                                    )}
                                />
                            </MediaUploadCheck>
                        </HStack>
                        }
                        </div>
                    </div>
                </PanelBody>
            </InspectorControls>
            <div {...blockProps} className='achievement'>
                <div className='achievement-wrap'>
                    <div className='achievement-image'>
                        <img
                            className="eb-feature-list-img"
                            src={attributes.mediaUrl} width="54"
                            alt=""
                        />
                    </div>
                    <div className='achievement-content'>
                        <RichText
                            tagName="h3"
                            value={ attributes.heading }
                            allowedFormats={ [ 'core/bold', 'core/italic' ] }
                            onChange={ ( heading ) => setAttributes( { heading } ) }
                            placeholder="Enter heading here..."
                        />
                        <RichText
                            tagName="p"
                            value={ attributes.content }
                            allowedFormats={ [ 'core/bold', 'core/italic' ] }
                            onChange={ ( content ) => setAttributes( { content } ) }
                            placeholder="Button text..."
                        />
                    </div>
                </div>
            </div>
        </Fragment>
    );
}

save.js

import { useBlockProps, RichText} from '@wordpress/block-editor';

export default function save( { attributes } ) {
    const blockProps = useBlockProps.save();
    return (
        <div {...useBlockProps.save()} className='achievement'>
            <div className='achievement-wrap'>
                <div className='achievement-image'>
                    <img
                        className="eb-feature-list-img"
                        src={attributes.mediaUrl} width="54"
                        alt=""
                    />
                </div>
                <div className='achievement-content'>
                    <RichText.Content tagName="h3" value={ attributes.heading } />
                    <RichText.Content tagName="p" value={ attributes.content } />
                </div>
            </div>
        </div>
    );
}

问题原因

你现在直接存储并输出图片URL(mediaUrl),而WordPress自动生成srcset需要依赖图片ID调用内部函数生成响应式属性,仅用URL无法触发这个机制。静态保存的HTML也无法动态生成适配不同屏幕的图片链接。

修复步骤

1. 更新block.json

移除冗余的mediaUrl属性,添加动态渲染指向PHP文件:

{
    "$schema": "https://schemas.wp.org/trunk/block.json",
    "apiVersion": 3,
    "name": "dt-panphagia/dt-panphagia",
    "version": "0.1.0",
    "title": "Panphagia",
    "category": "widgets",
    "icon": "admin-site-alt",
    "description": "",
    "keywords": [ "dt", "saurus", "archive" ],
    "supports": {
        "align": ["full", "wide"]
    },
    "textdomain": "dt-panphagia",
    "editorScript": "file:./index.js",
    "editorStyle": "file:./index.css",
    "style": "file:./style-index.css",
    "viewScript": "file:./view.js",
    "render": "file:./render.php",
    "attributes": {
        "content": {
            "type": "string",
            "selector": "p"
        },
        "heading": {
            "type": "string"
        },
        "mediaId": {
            "type": "number",
            "default": 0
        }
    }
}

2. 创建render.php动态渲染文件

用PHP调用WordPress内置函数生成带srcset的图片标签:

<?php
/**
 * 区块动态渲染模板
 */

$attributes = $block['attributes'];
$media_id = $attributes['mediaId'];
$heading = $attributes['heading'];
$content = $attributes['content'];

// 获取图片响应式属性
$image_src = wp_get_attachment_image_url( $media_id, 'medium' );
$image_srcset = wp_get_attachment_image_srcset( $media_id, 'medium' );
$image_alt = get_post_meta( $media_id, '_wp_attachment_image_alt', true );

?>
<div <?php echo get_block_wrapper_attributes( array( 'class' => 'achievement' ) ); ?>>
    <div class='achievement-wrap'>
        <div class='achievement-image'>
            <?php if ( $media_id ) : ?>
                <img
                    class="eb-feature-list-img"
                    src="<?php echo esc_url( $image_src ); ?>"
                    width="54"
                    alt="<?php echo esc_attr( $image_alt ?: '' ); ?>"
                    srcset="<?php echo esc_attr( $image_srcset ); ?>"
                    sizes="(max-width: 768px) 100vw, 54px"
                />
            <?php endif; ?>
        </div>
        <div class='achievement-content'>
            <?php if ( $heading ) : ?>
                <h3><?php echo wp_kses_post( $heading ); ?></h3>
            <?php endif; ?>
            <?php if ( $content ) : ?>
                <p><?php echo wp_kses_post( $content ); ?></p>
            <?php endif; ?>
        </div>
    </div>
</div>

3. 修改save.js返回null

动态区块不需要静态保存HTML内容:

export default function save() {
    return null;
}

4. 优化edit.js(可选)

移除冗余的属性存储逻辑,仅保留mediaId:

import { __ } from '@wordpress/i18n';
import { useBlockProps, RichText, MediaUpload, MediaUploadCheck, InspectorControls } from '@wordpress/block-editor';
import { PanelBody, Button, ResponsiveWrapper, __experimentalHStack as HStack } from '@wordpress/components';
import { Fragment } from '@wordpress/element'
import './editor.scss';

export default function Edit({ attributes, setAttributes }) {
    const blockProps = useBlockProps();

    const removeMedia = () => {
        setAttributes({ mediaId: 0 });
    }

    return (
        <Fragment>
            <InspectorControls>
                <PanelBody title={__('图片/成就', 'dt-panphagia')} initialOpen={true}>
                    <div className="editor-post-featured-image">
                        <div className='editor-post-featured-image__container'>
                            <MediaUploadCheck>
                                <MediaUpload
                                    onSelect={(media) => {
                                        setAttributes({ mediaId: media.id });
                                    }}
                                    type="image"
                                    value={attributes.mediaId}
                                    render={({ open }) => (
                                        <Button
                                            className={attributes.mediaId === 0 ? 'editor-post-featured-image__toggle' : 'editor-post-featured-image__preview'}
                                            onClick={open}
                                        >
                                            {attributes.mediaId === 0 && __('选择图片', 'dt-panphagia')}
                                            {attributes.mediaId !== 0 && (
                                                <ResponsiveWrapper naturalWidth={media.width} naturalHeight={media.height}>
                                                    <img src={media.url} alt={media.alt || ''} />
                                                </ResponsiveWrapper>
                                            )}
                                        </Button>
                                    )}
                                />
                            </MediaUploadCheck>
                            {attributes.mediaId !== 0 && (
                                <HStack className='editor-post-featured-image__actions'>
                                    <Button className='editor-post-featured-image__action' onClick={removeMedia}>{__('移除', 'dt-panphagia')}</Button>
                                    <MediaUploadCheck>
                                        <MediaUpload
                                            title={__('替换图片', 'dt-panphagia')}
                                            value={attributes.mediaId}
                                            onSelect={(media) => {
                                                setAttributes({ mediaId: media.id });
                                            }}
                                            type="image"
                                            render={({ open }) => (
                                                <Button className='editor-post-featured-image__action' onClick={open}>{__('替换', 'dt-panphagia')}</Button>
                                            )}
                                        />
                                    </MediaUploadCheck>
                                </HStack>
                            )}
                        </div>
                    </div>
                </PanelBody>
            </InspectorControls>
            <div {...blockProps} className='achievement'>
                <div className='achievement-wrap'>
                    <div className='achievement-image'>
                        {attributes.mediaId !== 0 && (
                            <img
                                className="eb-feature-list-img"
                                src={media.url}
                                width="54"
                                alt={media.alt || ''}
                            />
                        )}
                    </div>
                    <div className='achievement-content'>
                        <RichText
                            tagName="h3"
                            value={attributes.heading}
                            allowedFormats={['core/bold', 'core/italic']}
                            onChange={(heading) => setAttributes({ heading })}
                            placeholder="输入标题..."
                        />
                        <RichText
                            tagName="p"
                            value={attributes.content}
                            allowedFormats={['core/bold', 'core/italic']}
                            onChange={(content) => setAttributes({ content })}
                            placeholder="输入内容..."
                        />
                    </div>
                </div>
            </div>
        </Fragment>
    );
}

原理说明

WordPress的wp_get_attachment_image_srcset()函数会根据图片ID自动生成适配不同屏幕分辨率的图片链接集合,静态保存的URL无法触发这个逻辑,因此必须将区块改为动态渲染模式,通过PHP调用内置函数生成完整的响应式图片属性。

内容的提问来源于stack exchange,提问作者Giovanna Penna Eirada

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 00:02:05