gutenbergdocs/how-to-guides/block-tutorial/creating-dynamic-blocks.md
2025-10-22 01:33:45 +08:00

5.7 KiB
Raw Blame History

创建动态区块

动态区块是在前端渲染时实时构建结构和内容的区块。

动态区块主要有两个用途:

  1. 内容需要实时更新的区块即使文章未更新区块内容也会自动变化。WordPress 自带的「最新文章」区块就是典型示例——每当发布新文章时,所有使用该区块的位置都会同步更新。
  2. 需要即时呈现代码更新的区块当修改区块代码HTML/CSS/JS使用动态区块可确保修改立即在全站所有该区块实例上生效。若不使用动态区块Gutenberg 的验证流程会触发,导致用户看到“此区块似乎已被外部修改”的提示)

对于多数动态区块,save 回调函数应返回 null,这会让编辑器仅将区块属性保存至数据库。这些属性随后会传入服务端渲染回调,从而由开发者决定如何在前端展示区块。返回 null 时,编辑器会跳过区块标记验证流程,避免因频繁变化的标记引发问题。

若在动态区块中使用 InnerBlocks,需通过 <InnerBlocks.Content/>save 回调中保存内部区块内容。

也可保存区块的 HTML 表示形式。若设置了服务端渲染回调,该 HTML 会被回调输出替换;但当区块被停用或渲染回调被移除时,此 HTML 仍会正常渲染。

区块属性可用于保存任何需要存储的内容或设置。例如最新文章区块中,可将要显示的文章数量保存为属性;又或者针对需要在前端展示的每项内容(如标题文本、段落文字、图片、链接等),都可通过属性进行配置。

以下代码示例展示了如何创建仅显示最新文章链接的动态区块:

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

registerBlockType( 'gutenberg-examples/example-dynamic', {
	apiVersion: 3,
	title: '示例:最新文章',
	icon: 'megaphone',
	category: 'widgets',

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

		return (
			<div { ...blockProps }>
				{ ! posts && '加载中' }
				{ posts && posts.length === 0 && '暂无文章' }
				{ posts && posts.length > 0 && (
					<a href={ posts[ 0 ].link }>
						{ posts[ 0 ].title.rendered }
					</a>
				) }
			</div>
		);
	},
} );

由于是动态区块,无需在客户端重写默认的 save 实现,但需要配置服务端组件。前端显示内容取决于 register_block_typerender_callback 属性所调用的函数。

<?php

/**
 * 插件名称Gutenberg 动态区块示例
 */

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 '暂无文章';
	}
	$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() {
	// 自动加载依赖项与版本号
	$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' );

需要注意以下几点:

  • edit 函数仍在编辑器中展示区块预览(可与渲染版本完全不同,由区块作者决定)
  • 内置 save 函数直接返回 null,因为渲染在服务端完成
  • 服务端渲染函数接收区块属性及内部内容作为参数,返回标记(与短代码机制高度相似)

注意: 关于颜色、边框、间距等常见自定义设置,我们将在下一章了解如何通过区块支持功能高效实现。

在区块编辑器中实时渲染

Gutenberg 2.8 版本引入了 <ServerSideRender> 区块,支持在服务端使用 PHP 进行渲染而非 JavaScript。

服务端渲染仅作为降级方案,始终推荐使用客户端 JavaScript 渲染(速度更快且支持更灵活的编辑器操作)。

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: '示例:最新文章',
	icon: 'megaphone',
	category: 'widgets',

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

注意此代码使用了 wp-server-side-render 包但未使用 wp-data,请确保在 PHP 代码中更新依赖项。建议使用 wp-scripts 自动构建依赖(具体 PHP 代码配置可参考 block-development-examples 代码库)。