gutenbergdocs/docs/how-to-guides/block-tutorial/creating-dynamic-blocks.md

137 lines
5.7 KiB
Markdown
Raw Normal View History

2025-10-21 17:33:45 +00:00
# 创建动态区块
动态区块是在前端渲染时实时构建结构和内容的区块。
动态区块主要有两个用途:
1. **内容需要实时更新的区块**即使文章未更新区块内容也会自动变化。WordPress 自带的「最新文章」区块就是典型示例——每当发布新文章时,所有使用该区块的位置都会同步更新。
2. **需要即时呈现代码更新的区块**当修改区块代码HTML/CSS/JS使用动态区块可确保修改立即在全站所有该区块实例上生效。若不使用动态区块Gutenberg 的[验证流程](/docs/reference-guides/block-api/block-edit-save.md#validation)会触发,导致用户看到“此区块似乎已被外部修改”的提示)
对于多数动态区块,`save` 回调函数应返回 `null`,这会让编辑器仅将[区块属性](/docs/reference-guides/block-api/block-attributes.md)保存至数据库。这些属性随后会传入服务端渲染回调,从而由开发者决定如何在前端展示区块。返回 `null` 时,编辑器会跳过区块标记验证流程,避免因频繁变化的标记引发问题。
若在动态区块中使用 [InnerBlocks](/docs/how-to-guides/block-tutorial/nested-blocks-inner-blocks.md),需通过 `<InnerBlocks.Content/>``save` 回调中保存内部区块内容。
也可保存区块的 HTML 表示形式。若设置了服务端渲染回调,该 HTML 会被回调输出替换;但当区块被停用或渲染回调被移除时,此 HTML 仍会正常渲染。
区块属性可用于保存任何需要存储的内容或设置。例如最新文章区块中,可将要显示的文章数量保存为属性;又或者针对需要在前端展示的每项内容(如标题文本、段落文字、图片、链接等),都可通过属性进行配置。
以下代码示例展示了如何创建仅显示最新文章链接的动态区块:
```jsx
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_type``render_callback` 属性所调用的函数。
```php
<?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`,因为渲染在服务端完成
- 服务端渲染函数接收区块属性及内部内容作为参数,返回标记(与短代码机制高度相似)
**注意:** 关于颜色、边框、间距等常见自定义设置,我们将在[下一章](/docs/how-to-guides/block-tutorial/block-supports-in-dynamic-blocks.md)了解如何通过区块支持功能高效实现。
## 在区块编辑器中实时渲染
Gutenberg 2.8 版本引入了 [`<ServerSideRender>`](/packages/server-side-render/README.md) 区块,支持在服务端使用 PHP 进行渲染而非 JavaScript。
*服务端渲染仅作为降级方案,始终推荐使用客户端 JavaScript 渲染(速度更快且支持更灵活的编辑器操作)。*
```jsx
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 代码库](https://github.com/WordPress/block-development-examples/tree/trunk/plugins/basic-esnext-a2ab62))。