gutenbergdocs/getting-started/fundamentals/static-dynamic-rendering.md
2025-10-22 01:33:45 +08:00

178 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

### 数据库中动态区块的HTML呈现方式`save`方法)
对于动态区块,`save`回调函数可以直接返回`null`,这将告知编辑器仅将区块分隔注释(以及现有的[区块属性](https://developer.wordpress.org/block-editor/reference-guides/block-api/block-attributes/))保存至数据库。这些属性随后会被传递到服务端渲染回调函数,由该函数决定如何在前端站点上显示区块。
当`save`返回`null`时,区块编辑器将跳过[区块标记验证流程](https://developer.wordpress.org/block-editor/reference-guides/block-api/block-edit-save/#validation),避免因频繁变化的标记引发问题。
具有动态渲染功能的区块也可以保存区块的HTML表示作为备用方案。如果您提供了服务端渲染回调数据库中代表区块的HTML将被回调输出所替代但若区块被停用注册区块的插件被卸载或渲染回调被移除则会呈现原保存的HTML。
在某些情况下区块会保存其HTML表示并在满足特定条件时使用动态渲染来优化标记。核心区块中使用此方法的示例包括
- [封面区块](https://github.com/WordPress/gutenberg/blob/trunk/packages/block-library/src/cover)通过[save方法](https://github.com/WordPress/gutenberg/blob/trunk/packages/block-library/src/cover/save.js)在数据库中保存完整的HTML表示。该标记通过[`render_callback`](https://github.com/WordPress/gutenberg/blob/22741661998834e69db74ad863705ee2ce97b446/packages/block-library/src/cover/index.php#L74)处理,当启用“使用特色图像”设置时,会[动态注入](https://github.com/WordPress/gutenberg/blob/22741661998834e69db74ad863705ee2ce97b446/packages/block-library/src/cover/index.php#L16)特色图像。
- [图像区块](https://github.com/WordPress/gutenberg/blob/trunk/packages/block-library/src/image)同样通过[save方法](https://github.com/WordPress/gutenberg/blob/trunk/packages/block-library/src/image/save.js)将其HTML表示保存至数据库。该标记通过[`render_callback`](https://github.com/WordPress/gutenberg/blob/22741661998834e69db74ad863705ee2ce97b446/packages/block-library/src/image/index.php#L363)处理,当满足特定条件时,会[为标记添加额外属性](https://github.com/WordPress/gutenberg/blob/22741661998834e69db74ad863705ee2ce97b446/packages/block-library/src/image/index.php#L18)。
如果在动态区块中使用[内部区块](https://developer.wordpress.org/block-editor/how-to-guides/block-tutorial/nested-blocks-inner-blocks/),您需要通过`<InnerBlocks.Content/>`在`save`回调函数中保存`InnerBlocks`。
## 扩展资源
- [静态区块 vs 动态区块:差异解析](https://developer.wordpress.org/news/2023/02/27/static-vs-dynamic-blocks-whats-the-difference/) | 开发者博客
- [区块弃用机制详解](https://developer.wordpress.org/news/2023/03/10/block-deprecation-a-tutorial/) | 开发者博客
# 区块的静态与动态渲染
区块的前端标记既可以在请求时由服务器端动态生成(动态区块),也可以在区块编辑器的保存过程中静态生成(静态区块)。本文将深入探讨这两种实现方式。
<div class="callout callout-tip">
<a href="https://developer.wordpress.org/news/2023/02/27/static-vs-dynamic-blocks-whats-the-difference/">《静态区块与动态区块:差异解析》</a>这篇技术文章为本主题提供了绝佳的入门指引。
</div>
## 静态渲染
采用"静态渲染"的区块在保存时就会生成固定不变的前端输出并存储至数据库。这类区块完全依赖其 `save` 函数来定义其 [HTML 标记结构](https://developer.wordpress.org/block-editor/getting-started/fundamentals/markup-representation-block/),除非在区块编辑器中进行手动修改,否则这些标记将始终保持不变。
若某个区块未采用动态渲染方式——即不通过 PHP 在页面加载时实时生成内容——则会被归类为"静态区块"。
下图直观展示了静态区块内容如何保存至数据库,并在前端被检索并渲染为 HTML 的过程。
![静态渲染区块示意图](https://developer.wordpress.org/files/2024/01/static-rendering.png)
### 如何为区块定义静态渲染
通过[在客户端注册区块](https://developer.wordpress.org/block-editor/getting-started/fundamentals/registration-of-a-block/#registration-of-the-block-with-javascript-client-side)时可定义的 `save` 函数,可以指定区块的 HTML 结构。每当在编辑器中保存区块时,该 HTML 结构就会被存入数据库,并最终用于前端展示。
WordPress 中的区块会被封装在特殊的注释标签内,这些标签作为唯一的[区块定界符](https://developer.wordpress.org/block-editor/getting-started/fundamentals/markup-representation-block/)存在。但最终渲染时,只会呈现静态区块 `save` 函数中定义的 HTML 内容(不包含这些定界符)。
<details><summary><strong>查看预格式化区块中的静态渲染示例</strong></summary>
<br/>
以下是 <a href="https://github.com/WordPress/gutenberg/tree/trunk/packages/block-library/src/preformatted">预格式化</a> 核心区块的 <a href="https://github.com/WordPress/gutenberg/blob/trunk/packages/block-library/src/preformatted/save.js"><code>save</code> 函数</a>示例:
```js
import { RichText, useBlockProps } from '@wordpress/block-editor';
export default function save( { attributes } ) {
const { content } = attributes;
return (
<pre { ...useBlockProps.save() }>
<RichText.Content value={ content } />
</pre>
);
}
```
`attributes.content` 的值为 `"这是一段预格式化文本"` 时,该函数会生成如下区块标记表示:
```html
<!-- wp:preformatted -->
<pre class="wp-block-preformatted">这是一段预格式化文本</pre>
<!-- /wp:preformatted -->
```
在前端,该区块将返回以下标记。请注意定界符已不复存在。
```html
<pre class="wp-block-preformatted">这是一段预格式化文本</pre>
```
</details>
<br/>
我们将在下一节探讨的动态区块,虽然也能通过 `save` 函数指定初始 HTML 结构(类似于静态区块),但它们主要依赖服务器端渲染来生成内容。如果因故无法进行动态渲染(比如区块插件被停用),系统将转而使用数据库中保存的 HTML 结构来在前端显示该区块。
要了解具体运作机制,请参阅[构建你的第一个区块](/docs/getting-started/tutorial.md)教程。特别是[添加静态渲染](/docs/getting-started/tutorial.md#adding-static-rendering)章节,生动演示了区块如何同时具备保存 HTML 结构和动态渲染能力。
<div class="callout callout-info">
WordPress 提供了诸如 <a href="https://developer.wordpress.org/reference/functions/render_block/"><code>render_block</code></a><a href="https://developer.wordpress.org/block-editor/how-to-guides/block-tutorial/creating-dynamic-blocks/"><code>render_callback</code></a> 等机制,用于在区块最终呈现到前端之前修改其保存的 HTML。这些工具赋予开发者动态定制区块输出的能力以满足复杂交互场景的需求。
</div>
更多采用静态渲染的 WordPress 区块示例(即其输出在保存时即固定,不依赖服务器端处理)包括:
- [分隔符区块](https://github.com/WordPress/gutenberg/blob/trunk/packages/block-library/src/separator/save.js)
- [间距区块](https://github.com/WordPress/gutenberg/blob/trunk/packages/block-library/src/spacer/save.js)
- [按钮区块](https://github.com/WordPress/gutenberg/blob/trunk/packages/block-library/src/button/save.js)
## 动态渲染
具备“动态渲染”功能的区块旨在前端请求时实时生成其内容和结构。与在数据库中保存固定HTML结构的静态区块不同“动态区块”依赖服务器端处理来动态构建输出这使其具有高度灵活性非常适合需要频繁更新或依赖外部数据的内容。
下图展示了动态区块的表示形式如何在数据库中保存然后在前端检索并动态渲染为HTML。
![动态渲染区块示意图](https://developer.wordpress.org/files/2024/01/dynamic-rendering.png)
动态区块的常见应用场景包括:
1. **内容需在文章未更新时自动变化的区块**:例如[最新文章](https://github.com/WordPress/gutenberg/tree/trunk/packages/block-library/src/latest-posts)区块,每当有新文章发布时会自动更新。
2. **标记更新需立即在前端显示的区块**若通过添加新类、HTML元素或任何方式更新区块结构使用动态区块可确保这些更改立即应用到全站所有该区块实例。若无动态区块类似更新可能触发区块编辑器中的[验证错误](https://developer.wordpress.org/block-editor/reference-guides/block-api/block-edit-save/#validation)。
### 如何为区块定义动态渲染
区块可通过两种主要方式定义动态渲染:
1. 使用可传递给[`register_block_type()`](https://developer.wordpress.org/block-editor/getting-started/fundamentals/registration-of-a-block/#registration-of-the-block-with-php-server-side)函数的`render_callback`参数。这对于[纯PHP区块](/docs/getting-started/fundamentals/registration-of-a-block.md#php-only-blocks-with-auto-registration)是必需的。
2. 使用通常命名为`render.php`的独立PHP文件。该文件路径应在`block.json`文件中通过[`render`](https://developer.wordpress.org/block-editor/getting-started/fundamentals/block-json/#files-for-the-blocks-behavior-output-or-style)属性定义。
这两种方法都会接收以下数据:
- `$attributes`:区块的属性数组。
- `$content`:数据库中存储的区块标记(如有)。
- `$block`:表示已渲染区块的[WP_Block](https://developer.wordpress.org/reference/classes/wp_block/)类实例([区块元数据](https://developer.wordpress.org/block-editor/reference-guides/block-api/block-metadata/))。
<details><summary><strong>查看站点标题区块中的动态渲染示例</strong></summary>
<br/>
[站点标题](https://github.com/WordPress/gutenberg/tree/trunk/packages/block-library/src/site-title)区块使用以下[`render_callback`](https://github.com/WordPress/gutenberg/blob/trunk/packages/block-library/src/site-title/index.php)
```php
function render_block_core_site_title( $attributes ) {
$site_title = get_bloginfo( 'name' );
if ( ! $site_title ) {
return;
}
$tag_name = 'h1';
$classes = empty( $attributes['textAlign'] ) ? '' : "has-text-align-{$attributes['textAlign']}";
if ( isset( $attributes['style']['elements']['link']['color']['text'] ) ) {
$classes .= ' has-link-color';
}
if ( isset( $attributes['level'] ) ) {
$tag_name = 0 === $attributes['level'] ? 'p' : 'h' . (int) $attributes['level'];
}
if ( $attributes['isLink'] ) {
$aria_current = is_home() || ( is_front_page() && 'page' === get_option( 'show_on_front' ) ) ? ' aria-current="page"' : '';
$link_target = ! empty( $attributes['linkTarget'] ) ? $attributes['linkTarget'] : '_self';
$site_title = sprintf(
'<a href="%1$s" target="%2$s" rel="home"%3$s>%4$s</a>',
esc_url( home_url() ),
esc_attr( $link_target ),
$aria_current,
esc_html( $site_title )
);
}
$wrapper_attributes = get_block_wrapper_attributes( array( 'class' => trim( $classes ) ) );
return sprintf(
'<%1$s %2$s>%3$s</%1$s>',
$tag_name,
$wrapper_attributes,
// 如果是链接则已预转义
$attributes['isLink'] ? $site_title : esc_html( $site_title )
);
}
```
但该区块未定义`save`函数,从其[`index.js`](https://github.com/WordPress/gutenberg/blob/trunk/packages/block-library/src/site-title/index.js)文件可见,这意味着区块在数据库中的标记表示如下:
```html
<!-- wp:site-title /-->
```
在前端,`render_callback`会根据请求区块时服务器的具体值动态渲染区块标记。这些值包括当前站点标题、URL、链接目标等。
```
<h1 class="wp-block-site-title"><a href="https://www.wp.org" target="_self" rel="home">我的WordPress网站</a></h1>
```
</details>
<br/>