#### 优化 render.php 最后一步是优化 `render.php` 文件。如果 `currentYear` 属性与 `fallbackCurrentYear` 属性相同,则无需动态生成区块内容。相关内容已保存在数据库中,并可通过 `$content` 变量在 `render.php` 文件中调用。 因此,请更新该文件,使其在 `currentYear` 与 `fallbackCurrentYear` 不匹配时渲染生成的内容。 ```php $current_year = date( "Y" ); // 确定要显示的内容 if ( isset( $attributes['fallbackCurrentYear'] ) && $attributes['fallbackCurrentYear'] === $current_year ) { // 当前年份与备用值相同,直接使用数据库中保存的区块内容(由 save.js 函数生成) $block_content = $content; } else { // 当前年份与备用值不同,渲染更新后的区块内容 if ( ! empty( $attributes['startingYear'] ) && ! empty( $attributes['showStartingYear'] ) ) { $display_date = $attributes['startingYear'] . '–' . $current_year; } else { $display_date = $current_year; } $block_content = '
© ' . esc_html( $display_date ) . '
'; } echo wp_kses_post( $block_content ); ``` 大功告成!现在您已拥有一个同时支持动态与静态渲染的区块。 ## 收官之笔 恭喜您完成本教程并成功构建专属的版权日期区块!通过这段学习旅程,您已掌握 WordPress 区块开发的基础知识,现在可以开始创建自己的区块了。 本教程完整代码请参考 GitHub 上的 [区块开发示例库](https://github.com/WordPress/block-development-examples/tree/trunk/plugins/copyright-date-block-09aac3)。 若您希望精进技能、挑战更复杂的项目或紧跟 WordPress 最新动态,以下资源将助您提升区块开发能力: - [区块开发环境配置](https://developer.wordpress.org/block-editor/getting-started/devenv/) - [区块开发基础指南](https://developer.wordpress.org/block-editor/getting-started/fundamentals/) - [WordPress 开发者博客](https://developer.wordpress.org/news/) - [区块开发示例库](https://github.com/WordPress/block-development-examples) | GitHub 代码库 请记住:每位专家都曾是初学者。持续学习、勇于实践,最重要的是,享受使用 WordPress 构建作品的乐趣! # 教程:构建你的第一个区块 在本教程中,你将构建一个"版权日期区块"——这是一个基础但实用的区块,用于显示版权符号(©)、当前年份以及可选的起始年份。这类内容通常用于网站页脚。 本教程将引导你完成从使用 [`create-block`](https://developer.wordpress.org/block-editor/getting-started/devenv/get-started-with-create-block/) 包搭建区块插件脚手架到修改每个文件的完整流程。虽然具备WordPress开发经验会更有帮助,但这不是学习本教程的必要条件。 完成本指南后,你将清晰理解区块开发的基础知识,并掌握创建自定义WordPress区块的必要技能。 ## 你将构建什么 快速预览即将构建的内容。  你也可以在 [WordPress Playground](https://playground.wordpress.net/?blueprint-url=https://raw.githubusercontent.com/WordPress/block-development-examples/trunk/plugins/copyright-date-block-09aac3/_playground/blueprint.json) 中体验完成后的项目,或使用[快速入门指南](https://developer.wordpress.org/block-editor/getting-started/quick-start-guide/)将完整区块插件安装到本地WordPress环境中。 ## 前置要求 完成本教程需要: 1. 代码编辑器 2. Node.js开发工具 3. 本地WordPress环境 若缺少其中任何一项,[区块开发环境](https://developer.wordpress.org/block-editor/getting-started/devenv/)文档将帮助你完成配置。准备就绪后请返回继续学习。wp-env 创建本地WordPress开发环境。当然你也可以使用任何满足上述前置要求的开发环境。
wp-env,请通过终端进入本地WordPress安装目录的 plugins/ 文件夹再运行以下命令。
--variant=dynamic 标志,这表示要搭建动态渲染区块的脚手架。后续章节将介绍动态与静态渲染的区别,并为该区块添加静态渲染功能。
>
``` 与编辑器中的 `useBlockProps()` 函数类似,[`get_block_wrapper_attributes()`](https://developer.wordpress.org/reference/functions/get_block_wrapper_attributes/) 会在[区块包装元素](https://developer.wordpress.org/block-editor/getting-started/fundamentals/block-wrapper/#the-server-side-render-markup)中输出所有必要的 CSS 类和样式。只需更新内容部分即可。 您可以使用 `date( "Y" )` 在 PHP 中获取当前年份,修改后的 `render.php` 文件应如下所示: ```php>©
``` 保存文件后,请确认区块在编辑器和前端均能正确显示。 ### 清理文件 当使用 `create-block` 工具包搭建区块时,可能会包含一些非必需文件。在本教程中,该区块并未使用样式表或前端 JavaScript。请通过以下操作清理插件的 `src/` 目录: 1. 在 `edit.js` 文件中移除导入 `editor.scss` 的代码行 2. 在 `index.js` 文件中移除导入 `style.scss` 的代码行 3. 删除 editor.scss、style.scss 和 view.js 文件 最后请确保所有修改均已保存,然后终止 `npm run start` 命令。运行 `npm run build` 以优化代码并完成生产环境准备。 至此您已构建了一个功能完整的 WordPress 区块,但我们的工作尚未结束。后续章节将继续添加功能并启用静态渲染。 ## 添加区块属性 当前构建的版权日期区块仅显示当前年份,但如果需要同时显示起始年份该如何实现?  此功能需要用户在区块的某个位置输入起始年份,并且还应具备启用/禁用该功能的开关。 实现方式可以多样化,但都需要使用[区块属性](https://developer.wordpress.org/block-editor/reference-guides/block-api/block-attributes/)。属性允许您为区块存储自定义数据,这些数据随后可用于修改区块的标记。 要实现起始年份功能,您需要定义一个属性存储起始年份,另一个属性用于控制是否显示起始年份。 ### 更新 block.json 文件 区块属性通常在 [`block.json`](https://developer.wordpress.org/block-editor/getting-started/fundamentals/block-json/#data-storage-in-the-block-with-attributes) 文件中定义。请打开该文件,在 `example` 属性后添加以下配置段: ```json "example": {}, "attributes": { "showStartingYear": { "type": "boolean" }, "startingYear": { "type": "string" } }, ``` 定义属性时必须指明 `type` 类型。本例中 `showStartingYear` 需存储布尔值,因此设为 `boolean` 类型;`startingYear` 则设为字符串类型。 保存文件后,即可开始编辑器的调整工作。 ### 更新 edit.js 文件 打开 `edit.js` 文件,您需要完成两项任务: - 添加用户界面控件,允许用户输入起始年份、切换功能开关,并将这些设置存储为属性 - 根据已定义的属性更新区块显示内容 ### 更新 render.php 文件 虽然编辑器看起来很棒,但起始年份功能尚未添加到前端。让我们通过更新 `render.php` 文件来解决这个问题。 首先,添加一个名为 `$display_date` 的变量,并复制你在上面的 `Edit()` 函数中所做的操作。 这个变量应该显示 `startingYear` 属性和 `$current_year` 变量的值,用破折号分隔;如果 `showStartingYear` 属性为 `false`,则仅显示 `$current_year`。在 render.php 中暴露了三个变量,你可以使用它们来自定义块的输出:
$attributes(数组):块的属性。$content(字符串):块的默认内容。$block(WP_Block):块的实例。> ©
``` 保存文件,并确认正确的块内容现在出现在你网站的前端。 你现在已经成功构建了一个动态渲染的自定义块,它利用了块支持、WordPress 核心组件和自定义属性。在许多情况下,对于一个显示版权日期并具有一些额外功能的块来说,这已经足够了。 然而,在下一节中,你将向块添加静态渲染。这个练习将说明块数据是如何存储在 WordPress 中的,并在此插件意外禁用时提供回退方案。 ## 添加静态渲染 一个块可以利用动态渲染、静态渲染,或者两者兼用。到目前为止,你构建的块是动态渲染的。它的块标记和关联的属性存储在数据库中,但其 HTML 输出并未存储。 静态渲染的块将始终将块标记、属性和输出存储在数据库中。块还可以在数据库中存储静态输出,同时在前端动态进一步增强,这是两种方法的结合。 如果你在编辑器中切换到代码编辑器,你将看到以下内容。 ```html ``` 将其与静态渲染的块(如段落块)进行比较。 ```html这是一个测试。
``` 段落的 HTML 存储在文章内容中,并保存在数据库中。 你可以在[基础文档](https://developer.wordpress.org/block-editor/getting-started/fundamentals/static-dynamic-rendering/)中了解更多关于动态和静态渲染的内容。虽然大多数块要么是动态渲染,要么是静态渲染,但你可以构建一个同时使用两种方法的块。 ### 为什么要添加静态渲染? 当你向动态渲染的块添加静态渲染时,`render.php` 文件仍将控制前端的输出,但块的 HTML 内容将被保存在数据库中。这意味着,即使插件从网站中移除,内容仍然会保留。对于这个版权日期块,内容将恢复为一个自定义 HTML 块,你可以轻松地将其转换为段落块。  虽然并非在所有情况下都需要,但向动态渲染的块添加静态渲染可以在插件意外禁用时提供有用的回退方案。 此外,考虑一种情况,块标记包含在块模式或主题模板中。如果用户安装该主题或使用该模式时未安装版权日期块,他们将收到一个块不可用的通知,但内容仍将显示。 添加静态渲染也是探索块内容在 WordPress 中如何存储和渲染的好方法。 ## 文件结构审阅 在开始修改脚手架生成的区块之前,有必要先了解插件的文件结构。请用代码编辑器打开插件文件夹。  接下来查阅[区块文件结构](https://developer.wordpress.org/block-editor/getting-started/fundamentals/file-structure-of-a-block/)文档,详细了解每个文件的功能。如果现在觉得内容太多不必担心,本教程将逐步讲解每个文件的使用方法。save.js文件。后续教程中将会把这个文件添加到插件以实现静态渲染,敬请关注。
{ 'Copyright Date Block – hello from the saved content!' }
); } ``` 这段代码与原有的 `edit.js` 文件结构相似,更多细节可参考[区块包装器](https://developer.wordpress.org/block-editor/getting-started/fundamentals/block-wrapper/#the-save-components-markup)文档。 接着在 `index.js` 文件中导入这个 `save()` 函数,并为 `registerBlockType()` 函数添加 save 属性。以下是更新后文件的简化视图: ```js import save from './save'; ... registerBlockType( metadata.name, { icon: calendarIcon, edit: Edit, save } ); ```save 而非 save: save。
Copyright Date Block – hello from the saved content!
``` 使用静态渲染构建区块时经常会遇到验证错误,这属于正常现象。`save()` 函数的输出必须与文章内容中的HTML完全匹配,随着功能添加可能会出现不同步。只要在完整构建区块后没有验证错误,就表示大功告成。 ### 更新 save.js 接下来更新 `save()` 函数的输出以显示正确内容。首先采用与 `edit.js` 相同的实现方式: 1. 为函数添加 `attributes` 参数 2. 定义 `showStartingYear` 和 `startingYear` 变量 3. 定义 `currentYear` 变量 4. 根据 `currentYear`、`showStartingYear` 和 `startingYear` 的值定义 `displayDate` 变量 最终代码应如下所示: ```js export default function save( { attributes } ) { const { showStartingYear, startingYear } = attributes; const currentYear = new Date().getFullYear().toString(); let displayDate; if ( showStartingYear && startingYear ) { displayDate = startingYear + '–' + currentYear; } else { displayDate = currentYear; } return (© { displayDate }
); } ``` 保存文件并刷新编辑器,点击“尝试恢复区块”后更新页面。在代码编辑器中查看,区块标记现在应显示为: ```html© 2017–2023
``` 至此看似已完成所有工作——区块内容已以HTML形式存入数据库,前端输出也能动态渲染。但仍有几个问题需要解决。 试想这种情况:用户在2023年将区块添加到页面,然后在2024年重新编辑该页面。前端显示会正常更新,但在编辑器中会出现区块验证错误。因为 `save()` 函数识别当前是2024年,而数据库中存储的区块内容仍显示2023年。 我们将在下一节解决这个问题。 ##### 切换控件 接下来,我们添加一个用于开启或关闭起始年份功能的切换开关。您可以通过设置 `showStartingYear` 属性的 `ToggleControl` 组件实现。该组件应包含: 1. 设置为“显示起始年份”的 `label` 属性 2. 设置为 `showStartingYear` 属性的 `checked` 属性 3. 在切换开关被勾选或取消时更新 `showStartingYear` 属性的 `onChange` 属性 您还可以更新“起始年份”文本输入框,使其仅在 `showStartingYear` 为 `true` 时显示,这可以通过 `&&` 逻辑运算符实现。 `Edit()` 函数应如下所示。 ```js export default function Edit( { attributes, setAttributes } ) { const { showStartingYear, startingYear } = attributes; const currentYear = new Date().getFullYear().toString(); return ( <>© { currentYear }
> ); } ``` 保存文件并刷新编辑器。确认点击切换开关会显示文本输入框,并且在更新页面时切换开关保持激活状态。  #### 更新区块内容 到目前为止,您已经创建了用于添加起始年份并更新相关区块属性的用户界面。现在需要实际更新编辑器中的区块内容。 我们创建一个名为 `displayDate` 的新变量。当 `showStartingYear` 为 `true` 且用户提供了 `startingYear` 时,`displayDate` 应包含由破折号连接的 `startingYear` 和 `currentYear`。否则,仅显示 `currentYear`。 代码应如下所示。 ```js let displayDate; if ( showStartingYear && startingYear ) { displayDate = startingYear + '–' + currentYear; } else { displayDate = currentYear; } ```let 声明变量时,表示该变量后续可能会被重新赋值。使用 const 声明变量则表示该变量永远不会改变。您也可以使用 const 重写此代码,这仅是个人偏好问题。
© { displayDate }
> ); } ``` 保存文件并刷新编辑器。确认当您在设置面板中进行更改时,区块内容会正确更新。  ### 处理静态渲染区块中的动态内容 通常来说,您需要避免在静态渲染区块中使用动态内容。这也是为什么在提及动态渲染时会使用"动态"这个术语的原因之一。 不过在本教程中,您将结合使用两种渲染方法,只需要添加少量代码就能在年份变更时避免区块验证错误。 问题的根源在于`currentYear`变量是在`save()`函数中动态设置的。实际上,这应该是函数内的静态变量,可以通过添加属性来解决这个问题。 #### 添加新属性 打开`block.json`文件,添加一个名为`fallbackCurrentYear`、类型为`string`的新属性。此时文件的`attributes`部分应如下所示: ```json "attributes": { "fallbackCurrentYear": { "type": "string" }, "showStartingYear": { "type": "boolean" }, "startingYear": { "type": "string" } }, ``` 接下来打开`save.js`文件,使用新的`fallbackCurrentYear`属性替代`currentYear`。更新后的`save()`函数应如下所示: ```js export default function save( { attributes } ) { const { fallbackCurrentYear, showStartingYear, startingYear } = attributes; let displayDate; if ( showStartingYear && startingYear ) { displayDate = startingYear + '–' + fallbackCurrentYear; } else { displayDate = fallbackCurrentYear; } return (© { displayDate }
); } ``` 现在,如果`fallbackCurrentYear`未定义会发生什么? 之前`currentYear`是在函数内定义的,因此即使`showStartingYear`和`startingYear`未定义,`save()`函数也始终有内容可以返回。 与其只返回版权符号,不如添加一个条件判断:如果`fallbackCurrentYear`未设置,则返回`null`。通常来说,在数据库中保存空内容比保存不完整的数据更好。 最终的`save()`函数应如下所示: ```js export default function save( { attributes } ) { const { fallbackCurrentYear, showStartingYear, startingYear } = attributes; if ( ! fallbackCurrentYear ) { return null; } let displayDate; if ( showStartingYear && startingYear ) { displayDate = startingYear + '–' + fallbackCurrentYear; } else { displayDate = fallbackCurrentYear; } return (© { displayDate }
); } ``` 保存`block.json`和`save.js`文件,您无需再进行其他修改。 #### 在edit.js中设置属性 既然`save()`函数现在使用新的`fallbackCurrentYear`,就需要在某个地方设置这个值。让我们使用`Edit()`函数来实现。 打开`edit.js`文件,首先在`Edit()`函数顶部与其他属性一起定义`fallbackCurrentYear`变量。接着检查函数中的现有逻辑。 当区块在编辑器中加载时,会定义`currentYear`变量。函数随后使用该变量来设置区块内容。 现在,让我们在区块加载时,如果`fallbackCurrentYear`属性尚未设置,就将其设置为`currentYear`的值: ```js if ( currentYear !== fallbackCurrentYear ) { setAttributes( { fallbackCurrentYear: currentYear } ); } ``` 这样虽然可行,但可以通过确保此代码仅在区块初始化时运行一次来进行优化。为此,您可以使用React的[`useEffect`](https://react.dev/reference/react/useEffect)钩子。有关如何使用此钩子的更多信息,请参阅React文档。 首先通过以下代码导入`useEffect`: ```js import { useEffect } from 'react'; ``` 然后将上面的`setAttribute()`代码包装在`useEffect`中,并将这段代码放在`Edit()`函数的`currentYear`定义之后。最终结果应如下所示: ```js export default function Edit( { attributes, setAttributes } ) { const { fallbackCurrentYear, showStartingYear, startingYear } = attributes; // 获取当前年份并确保其为字符串类型 const currentYear = new Date().getFullYear().toString(); // 当区块加载时,如果fallbackCurrentYear尚未设置, // 则将其属性设置为当前年份 useEffect( () => { if ( currentYear !== fallbackCurrentYear ) { setAttributes( { fallbackCurrentYear: currentYear } ); } }, [ currentYear, fallbackCurrentYear, setAttributes ] ); ... ``` 当区块在编辑器中初始化时,`fallbackCurrentYear`属性将立即被设置。这个值随后可供`save()`函数使用,正确的区块内容将显示且不会出现区块验证错误。 唯一需要注意的是年份变更的情况。如果版权日期区块在2023年添加到页面,然后在2024年进行编辑,`fallbackCurrentYear`属性将不再等于`currentYear`,该属性将自动更新为`2024`。这将更新`save()`函数返回的HTML。 虽然您不会遇到任何区块验证错误,但编辑器会检测到页面已发生更改,并提示您进行更新。 #### 添加用户界面 在本教程的前面部分,您添加了区块支持功能,这些功能会在区块的设置侧边栏中自动创建颜色和排版面板。您可以使用 `InspectorControls` 组件创建自定义面板。 ##### 检查器控件 `InspectorControls` 属于 [`@wordpress/block-editor`](https://developer.wordpress.org/block-editor/reference-guides/packages/packages-block-editor/) 包,因此您可以通过在第 14 行添加组件名称将其导入到 `edit.js` 文件中。结果应如下所示: ```js import { InspectorControls, useBlockProps } from '@wordpress/block-editor'; ``` 接下来,更新 Edit 函数,使其返回当前区块内容和一个包含“测试”文本的 `InspectorControls` 组件。您可以将所有内容包装在 [Fragment](https://react.dev/reference/react/Fragment) (`<>>`) 中,以确保 JSX 语法正确。结果应如下所示: ```js export default function Edit() { const currentYear = new Date().getFullYear().toString(); return ( <>© { currentYear }
> ); } ``` 保存文件并刷新编辑器。选择区块时,您应在设置侧边栏中看到“测试”消息。  ##### 组件和面板 现在,让我们使用更多核心组件来添加自定义面板和起始年份功能的用户界面。您需要从 [`@wordpress/components`](https://developer.wordpress.org/block-editor/reference-guides/packages/packages-components/) 包中导入 [`PanelBody`](https://developer.wordpress.org/block-editor/reference-guides/components/panel/#panelbody)、[`TextControl`](https://developer.wordpress.org/block-editor/reference-guides/components/text-control/) 和 [`ToggleControl`](https://developer.wordpress.org/block-editor/reference-guides/components/toggle-control/)。 在 `edit.js` 文件的其他导入下方添加以下行: ```js import { PanelBody, TextControl, ToggleControl } from '@wordpress/components'; ``` 然后将“测试”消息包装在 `PanelBody` 组件中,并将 `title` 参数设置为“设置”。有关其他参数选项,请参阅[组件文档](https://developer.wordpress.org/block-editor/reference-guides/components/panel/#panelbody)。 ```js export default function Edit() { const currentYear = new Date().getFullYear().toString(); return ( <>© { currentYear }
> ); } ``` 保存文件并刷新编辑器。您现在应该可以看到新的设置面板。  ##### 文本控件 下一步是将“测试”消息替换为 `TextControl` 组件,该组件允许用户设置 `startingYear` 属性。但在执行此操作之前,您必须在 `Edit()` 函数中包含两个参数: - `attributes` 是一个包含区块所有属性的对象。 - `setAttributes` 是一个允许您更新属性值的函数。 包含这些参数后,您可以获取 `showStartingYear` 和 `startingYear` 属性。 更新 `Edit()` 函数的开头部分,使其如下所示: ```js export default function Edit( { attributes, setAttributes } ) { const { showStartingYear, startingYear } = attributes; ... ```Edit() 函数的开头添加 console.log( attributes );。这在构建和测试自定义区块时非常有用。
© { currentYear }
> ); } ```value 属性的值为 startingYear || ''。符号 || 称为 逻辑或(逻辑析取)运算符。这可以防止当 startingYear 为空时在 React 中出现警告。详情请参阅 受控和非受控组件。
viewBox参数。
{ __( 'Copyright Date Block – hello from the editor!', 'copyright-date-block-demo' ) }
); } ``` 其实际逻辑比表面看起来更简洁: - [`useBlockProps()`](https://developer.wordpress.org/block-editor/reference-guides/block-api/block-edit-save/#block-wrapper-props)用于输出编辑器所需的[区块包装器](https://developer.wordpress.org/block-editor/getting-started/fundamentals/block-wrapper/#the-edit-components-markup)中的所有CSS类与样式,包含先前添加的区块支持功能提供的样式 - [`__()`](https://developer.wordpress.org/block-editor/reference-guides/packages/packages-i18n/)用于实现文本字符串的国际化© { currentYear }
); } ``` 保存`edit.js`文件并刷新编辑器,您现在将看到版权符号(©)后接当前年份的显示效果。 