块编辑器开发文档

创建动态区块

💡 云策文档标注

概述

动态区块是在前端渲染时动态构建结构和内容的区块,适用于内容需实时更新或代码变更需立即生效的场景。通过服务器端渲染回调函数,开发者可以灵活控制区块的显示逻辑,避免编辑器验证问题。

关键要点

  • 动态区块主要用于两类场景:内容需自动更新(如最新文章区块)和代码变更需立即在前端生效(如添加新类或HTML元素)。
  • save 回调函数通常返回 null,仅保存区块属性到数据库,由服务器端渲染回调函数决定前端显示,跳过编辑器标记验证。
  • 使用 InnerBlocks 时,需在 save 回调函数中使用 <InnerBlocks.Content/> 保存内容。
  • 区块属性可用于保存任何内容或设置,如显示文章数量或文本内容。
  • 服务器端渲染通过 render_callback 属性实现,类似于短代码,接收区块属性和内容作为参数并返回标记。
  • 编辑器中的实时渲染可使用 <ServerSideRender> 组件作为后备方案,但客户端渲染更受推荐。

代码示例

import { registerBlockType } from '@wordpress/blocks';
import { useSelect } from '@wordpress/data';
import { useBlockProps } from '@wordpress/block-editor';

registerBlockType( 'gutenberg-examples/example-dynamic', {
    apiVersion: 3,
    title: 'Example: last post',
    icon: 'megaphone',
    category: 'widgets',

    edit: () => {
        const blockProps = useBlockProps();
        const posts = useSelect( ( select ) => {
            return select( 'core' ).getEntityRecords( 'postType', 'post' );
        }, [] );

        return (
            <div { ...blockProps }>
                { ! posts && 'Loading' }
                { posts && posts.length === 0 && 'No Posts' }
                { posts && posts.length > 0 && (
                    <a href={ posts[ 0 ].link }>
                        { posts[ 0 ].title.rendered }
                    </a>
                ) }
            </div>
        );
    },
} );

注意事项

  • edit 函数在编辑器中显示区块表示,可能与渲染版本不同,由区块作者决定。
  • 内置 save 函数返回 null,因为渲染在服务器端执行。
  • 服务器端渲染函数接收区块和内部内容作为参数,返回标记,类似于短代码。
  • 对于颜色、边框、间距等常见自定义设置,可依赖区块支持功能高效实现。
  • 使用 <ServerSideRender> 时,注意更新 PHP 代码中的依赖项,可使用 wp-scripts 自动构建。

📄 原文内容

Dynamic blocks are blocks that build their structure and content on the fly when the block is rendered on the front end.

There are two primary uses for dynamic blocks:

  1. Blocks where content should change even if a post has not been updated. One example from WordPress itself is the Latest Posts block. This block will update everywhere it is used when a new post is published.
  2. Blocks where updates to the code (HTML, CSS, JS) should be immediately shown on the front end of the website. For example, if you update the structure of a block by adding a new class, adding an HTML element, or changing the layout in any other way, using a dynamic block ensures those changes are applied immediately on all occurrences of that block across the site. (If a dynamic block is not used then when block code is updated Gutenberg’s validation process generally applies, causing users to see the validation message, “This block appears to have been modified externally”).

For many dynamic blocks, the save callback function should be returned as null, which tells the editor to save only the block attributes to the database. These attributes are then passed into the server-side rendering callback, so you can decide how to display the block on the front end of your site. When you return null, the editor will skip the block markup validation process, avoiding issues with frequently-changing markup.

If you are using InnerBlocks in a dynamic block you will need to save the InnerBlocks in the save callback function using <InnerBlocks.Content/>

You can also save an HTML representation of the block. If you provide a server-side rendering callback, this HTML will be replaced with the output of your callback, but will be rendered if your block is deactivated or your render callback is removed.

Block attributes can be used for any content or setting you want to save for that block. In the first example above, with the latest posts block, the number of latest posts you want to show could be saved as an attribute. Or in the second example, attributes can be used for each piece of content you want to show in the front end – such as heading text, paragraph text, an image, a URL, etc.

The following code example shows how to create a dynamic block that shows only the last post as a link.

import { registerBlockType } from '@wordpress/blocks';
import { useSelect } from '@wordpress/data';
import { useBlockProps } from '@wordpress/block-editor';

registerBlockType( 'gutenberg-examples/example-dynamic', {
    apiVersion: 3,
    title: 'Example: last post',
    icon: 'megaphone',
    category: 'widgets',

    edit: () => {
        const blockProps = useBlockProps();
        const posts = useSelect( ( select ) => {
            return select( 'core' ).getEntityRecords( 'postType', 'post' );
        }, [] );

        return (
            <div { ...blockProps }>
                { ! posts && 'Loading' }
                { posts && posts.length === 0 && 'No Posts' }
                { posts && posts.length > 0 && (
                    <a href={ posts[ 0 ].link }>
                        { posts[ 0 ].title.rendered }
                    </a>
                ) }
            </div>
        );
    },
} );

Because it is a dynamic block it doesn’t need to override the default save implementation on the client. Instead, it needs a server component. The contents in the front of your site depend on the function called by the render_callback property of register_block_type.

<?php

/**
 * Plugin Name: Gutenberg examples dynamic
 */

function gutenberg_examples_dynamic_render_callback( $block_attributes, $content ) {
    $recent_posts = wp_get_recent_posts( array(
        'numberposts' => 1,
        'post_status' => 'publish',
    ) );
    if ( count( $recent_posts ) === 0 ) {
        return 'No posts';
    }
    $post = $recent_posts[ 0 ];
    $post_id = $post['ID'];
    return sprintf(
        '<a class="wp-block-my-plugin-latest-post" href="%1$s">%2$s</a>',
        esc_url( get_permalink( $post_id ) ),
        esc_html( get_the_title( $post_id ) )
    );
}

function gutenberg_examples_dynamic() {
    // automatically load dependencies and version
    $asset_file = include( plugin_dir_path( __FILE__ ) . 'build/index.asset.php');

    wp_register_script(
        'gutenberg-examples-dynamic',
        plugins_url( 'build/block.js', __FILE__ ),
        $asset_file['dependencies'],
        $asset_file['version']
    );

    register_block_type( 'gutenberg-examples/example-dynamic', array(
        'api_version' => 3,
        'editor_script' => 'gutenberg-examples-dynamic',
        'render_callback' => 'gutenberg_examples_dynamic_render_callback'
    ) );

}
add_action( 'init', 'gutenberg_examples_dynamic' );

There are a few things to notice:

  • The edit function still shows a representation of the block in the editor’s context (this could be very different from the rendered version, it’s up to the block’s author)
  • The built-in save function just returns null because the rendering is performed server-side.
  • The server-side rendering is a function taking the block and the block inner content as arguments, and returning the markup (quite similar to shortcodes)

Note : For common customization settings including color, border, spacing customization and more, we will see on the next chapter how you can rely on block supports to provide such functionality in an efficient way.

Live rendering in the block editor

Gutenberg 2.8 added the <ServerSideRender> block which enables rendering to take place on the server using PHP rather than in JavaScript.

Server-side render is meant as a fallback; client-side rendering in JavaScript is always preferred (client rendering is faster and allows better editor manipulation).

import { registerBlockType } from '@wordpress/blocks';
import ServerSideRender from '@wordpress/server-side-render';
import { useBlockProps } from '@wordpress/block-editor';

registerBlockType( 'gutenberg-examples/example-dynamic', {
    apiVersion: 3,
    title: 'Example: last post',
    icon: 'megaphone',
    category: 'widgets',

    edit: function ( props ) {
        const blockProps = useBlockProps();
        return (
            <div { ...blockProps }>
                <ServerSideRender
                    block="gutenberg-examples/example-dynamic"
                    attributes={ props.attributes }
                />
            </div>
        );
    },
} );

Note that this code uses the wp-server-side-render package but not wp-data. Make sure to update the dependencies in the PHP code. You can use wp-scripts to automatically build dependencies (see the block-development-examples repo for PHP code setup).