如何为 Gutenberg 创建动态块

已发表: 2022-08-03

你还在为古腾堡感到困惑吗? 或者您是那些坚信块编辑器的潜力并想知道使用块编辑器可以将他们的创造力推到多远的人中的一员?

无论您属于哪种类型的用户,Gutenberg 都会留下来,这篇文章将让您深入了解 WordPress 块编辑器幕后发生的事情。 但这还不是全部!

在我们之前的教程中我们提供了对 Gutenberg 块开发的一般介绍之后,本文超越了基础知识,介绍了更高级的块类型。 这些块被称为动态块。

今天,您将了解什么是动态块、它们是如何工作的,以及从头开始创建动态块所需了解的一切。

那么,什么是 Gutenberg 动态块,静态块和动态块之间的主要区别是什么?

什么是动态块? 一个例子

使用静态块时,用户在编辑帖子或页面时手动添加内容,而使用动态块时,内容会在页面加载时动态加载和处理。 使用动态块,块内容从数据库中提取并按原样显示或由任何类型的数据操作产生。

让我们用一个例子来解释一下。 假设您要创建一组嵌套块,显示帖子作者详细信息以及来自同一作者的最新帖子的选择。

一个组块,包括帖子作者和最新帖子
一个组块,包括帖子作者和最新帖子

作为 Gutenberg 用户,您可以使用以下块:

  • 标题核心块
  • 帖子作者核心块
  • 最新帖子核心块

您还可以创建一个包含这些块的组,并将该组添加到可重复使用的块中以供将来使用。

将组块添加到可重用块
将组块添加到可重用块

这很简单,不是吗? 您可以创建一个动态块并将其添加到您的帖子和页面中。

从 WordPress 5.9 开始,块编辑器提供了 90 多个不同的块,您很可能会立即找到适合您的块。 而且,如果您需要更多,请在 WordPress 插件目录中快速搜索,您会发现很多免费插件提供了额外的块。

但是,如果您是 WordPress 开发人员,或者您正计划从事 WordPress 开发人员的职业,该怎么办? 也许您有非常特定的需求,找不到您正在寻找的区块,或者您只是想获得新的专业技能。 在这种情况下,您可能想学习如何创建动态块。

准备好将您作为 WordPress 开发人员的职业带到月球了吗? 从这个庞大的动态块开发指南开始! 点击推文

从开发人员的角度来看 Gutenberg 动态块

动态块有两个主要用例。

第一个用例是当包含块的页面尚未更新时,您需要更新块的内容。 例如,当块包含最新帖子或评论的列表时会发生这种情况,并且通常每当块的内容是使用从数据库检索的数据动态生成时。

添加查询循环块
添加查询循环块

第二个用例是需要立即在前端显示对块代码的更新。 使用动态块而不是静态块会导致更改立即应用于所有出现的块。

另一方面,如果您要更改由静态块生成的 HTML,用户将看到一个无效对话框,直到该块的先前版本的每个实例都被删除并替换为新版本,或者您将旧版本标记为不推荐使用的版本(另请参阅弃用和块验证、弃用和迁移体验)。

意外或无效的内容。
意外或无效的内容。

话虽如此,在开始构建动态块之前,您需要了解一些概念。

应用程序状态和数据存储

Gutenberg 是一个 React SPA 应用程序,Gutenberg 中的所有内容都是一个 React 组件。 编辑器中的帖子标题、标题、段落、图像和任何 HTML 内容块都是 React 组件,以及侧边栏和块工具栏控件。

在我们之前的文章中,我们只使用属性来存储数据。 在本文中,我们将通过引入state的概念更进一步。

简而言之, state对象是一个普通的 JavaScript 对象,用于包含有关组件的信息。 组件的state会随着时间而改变,并且任何时候改变,组件都会重新渲染。

state对象类似,属性是普通的 JavaScript 对象,用于保存有关组件的信息。 但是 props 和state之间有一个关键区别:

props被传递给组件(类似于函数参数),而state是在组件内管理的(类似于在函数中声明的变量)。

您可以将状态视为在给定时间点获取的数据快照,应用程序存储这些数据以控制组件的行为。 例如,如果块编辑器设置侧边栏打开,一条信息将存储在state对象的某处。

当信息在单个组件内共享时,我们称之为本地状态。 当信息在应用程序内的组件之间共享时,我们称之为应用程序状态

Application State 与 store 的概念密切相关。 根据 Redux 文档:

一个 store 保存着应用程序的整个状态树。 更改其内部状态的唯一方法是在其上分派一个操作。

因此,Redux 将应用程序状态存储在单个不可变对象树(即存储)中。 对象树只能通过使用 action 和 reducer 创建新对象来更改。

在 WordPress 中,商店由WordPress 数据模块管理。

Gutenberg 中的模块化、包和数据存储

Gutenberg 存储库从头开始构建在几个可重用且独立的模块上,这些模块组合在一起构建了编辑界面。 这些模块也称为

官方文档列出了两种不同类型的包:

  • 生产包组成了在浏览器中运行的生产代码。 WordPress 中有两种类型的生产包:
    • 带有样式表的包提供了正确运行的样式表。
    • 带有数据存储的包定义了数据存储来处理它们的状态。 第三方插件和主题可以使用带有数据存储的包来检索和操作数据。
  • 开发包用于开发模式。 这些包包括用于 linting、测试、构建等的工具。

在这里,我们最感兴趣的是带有数据存储的包,用于检索和操作数据。

WordPress 数据存储

WordPress 数据模块建立在 Redux 之上,并共享三个 Redux 核心原则,尽管有一些关键区别。

官方文档提供了如下定义:

WordPress 的数据模块用作管理插件和 WordPress 本身的应用程序状态的中心,提供工具来管理不同模块内和模块之间的数据。 它被设计为一种用于组织和共享数据的模块化模式:简单到足以满足小型插件的需求,同时可扩展以服务于复杂的单页应用程序的需求。

默认情况下,Gutenberg 在应用程序状态中注册多个数据存储。 这些商店中的每一个都有特定的名称和用途:

  • core :WordPress 核心数据
  • core/annotations :注释
  • core/blocks :块类型数据
  • core/block-editor : 块编辑器的数据
  • core/editor :帖子编辑的数据
  • core/edit-post : 编辑器的 UI 数据
  • core/notices :通知数据
  • core/nux :NUX(新用户体验)数据
  • core/viewport :视口数据

通过这些商店,您将能够访问一大堆数据:

  1. 与当前帖子相关的数据,例如帖子标题、摘录、类别和标签、块等。
  2. 与用户界面相关的数据,即开关是打开还是关闭。
  3. 与整个 WordPress 安装相关的数据,例如注册分类法、帖子类型、博客标题、作者等。

这些存储存在于全局wp对象中。 要访问商店的状态,您将使用select函数。

要查看它是如何工作的,请创建一个新帖子或页面并启动浏览器的检查器。 找到控制台并输入以下代码行:

 wp.data.select("core")

结果将是一个对象,其中包含可用于从core数据存储中获取数据的函数列表。 这些函数称为选择器并充当访问状态值的接口。

核心 WordPress 数据存储对象
核心 WordPress 数据存储对象

WordPress 数据存储通常包含有关 WordPress 的信息,选择器是您获取该信息的方式。 例如, getCurrentUser()返回当前用户的详细信息:

 wp.data.select("core").getCurrentUser()
检查 getCurrentUser 响应
检查 getCurrentUser 响应

另一个可用于从数据存储中检索用户详细信息的选择器是getUsers()

 wp.data.select("core").getUsers()

下图显示了响应对象:

检查 getUsers 响应
检查 getUsers 响应

要获取单个用户的详细信息,您只需键入以下行:

 wp.data.select("core").getUsers()[0]

使用相同的选择器,您还可以检索分配了author角色的站点用户:

 wp.data.select( 'core' ).getUsers({ who: 'authors' })

您还可以检索已注册的分类法:

 wp.data.select("core").getTaxonomies()
检查 getTaxonomies 响应。
检查 getTaxonomies 响应。

已注册的帖子类型列表:

 wp.data.select("core").getPostTypes()

或插件列表:

 wp.data.select("core").getPlugins()

现在让我们尝试访问不同的数据存储。 为此,您仍将使用select函数,但提供不同的命名空间。 让我们尝试以下方法:

 wp.data.select("core/edit-post")

现在您将获得以下响应对象。

访问编辑器的 UI 数据
访问编辑器的 UI 数据

如果您想知道设置侧边栏是否打开,您可以使用isEditorSidebarOpened选择器:

 wp.data.select("core/edit-post").isEditorSidebarOpened()

如果侧边栏打开,此函数返回true

侧边栏已打开。
侧边栏已打开。

如何访问帖子数据

您现在应该对如何访问数据有了基本的了解。 现在我们将仔细研究一个特定的选择器, getEntityRecords函数,它是提供对发布数据的访问权限的选择器。

在块编辑器中,右键单击并选择Inspect 。 在控制台选项卡中,复制并粘贴以下行:

 wp.data.select("core").getEntityRecords('postType', 'post')

这会向 Rest API 发送请求,并返回与上次发布的博客文章相对应的记录数组。

getEntityRecords 返回帖子列表。
getEntityRecords 返回帖子列表。

getEntityRecords接受三个参数:

  • kind string :实体种类(即postType )。
  • name字符串:实体名称(即post )。
  • query ?Object : 可选术语查询(即{author: 0} )。

您可以使用参数对象构建更具体的请求。

例如,您可以决定响应应该只包含指定类别的帖子:

 wp.data.select("core").getEntityRecords('postType', 'post', {categories: 3})

您还可以仅请求给定作者的文章:

 wp.data.select("core").getEntityRecords('postType', 'post', {author: 2})

如果单击getEntityRecords返回的任何记录,您将获得所选记录的属性列表:

带有 getEntityRecords 的示例 API 请求。
带有 getEntityRecords 的示例 API 请求。

如果您希望响应包含特色图片,则需要在之前的请求中添加一个附加参数:

 wp.data.select("core").getEntityRecords('postType', 'post', {author: 2, _embed: true})
getEntityRecords 响应中的特色图像详细信息。
getEntityRecords 响应中的特色图像详细信息。

现在您应该对如何访问 WordPress 数据存储和检索帖子详细信息有了更好的了解。 有关getEntityRecords选择器的详细视图,另请参阅在 Gutenberg 中使用 getEntityRecords 请求数据。

如何创建动态块:示例项目

在我们长期的理论前提之后,我们可以继续练习并使用我们在之前的块开发教程中介绍的工具创建一个动态块。

在那篇文章中,我们讨论了:

  1. 如何设置 WordPress 开发环境
  2. 什么是块式脚手架
  3. 如何构建静态古腾堡块

这就是为什么我们不会在本文中深入讨论这些主题,但请随时参考我们之前的指南以获取任何其他信息,或者只是为了复习。

设置 JavaScript 开发环境

让我们从设置 JavaScript 开发环境开始。

安装或更新 Node.js

首先,安装或更新 Node.js。 完成后,启动命令行工具并运行以下命令:

 node -v

您应该看到您的节点版本。

设置您的开发环境

接下来,您需要一个 WordPress 开发环境。 对于我们的示例,我们使用了 DevKinsta,这是我们的免费 WordPress 开发工具,可让您立即启动本地 WordPress 网站。

在 DevKinsta 中创建自定义站点
在 DevKinsta 中创建自定义站点

但是您仍然可以自由选择任何您喜欢的 WordPress 本地开发环境,例如 MAMP 或 XAMPP,甚至是官方的 wp-env 解决方案。

如果您使用的是 DevKinsta,请单击New WordPress SiteCustom Site ,填写表单字段并按下Create site

安装过程需要一两分钟。 完成后,启动您当地的 WordPress 开发网站。

DevKinsta 中的站点信息屏幕。
DevKinsta 中的站点信息屏幕。

设置你的块插件

您现在需要的是一个入门块插件。 为了避免手动配置的所有麻烦,WordPress 核心开发团队发布了@wordpress/create-block 工具,这是用于创建古腾堡块的官方零配置工具

我们在上一篇文章中深入介绍了@wordpress/create-block ,所以在这里我们可以立即开始设置。

在命令行工具中,导航到/wp-content/plugins文件夹:

Mac OS 文件夹中的新终端。
Mac OS 文件夹中的新终端。

在那里,运行以下命令:

 npx @wordpress/create-block

您现在已准备好安装@wordpress/create-block包:

安装 @wordpress/create-block 包。
安装 @wordpress/create-block 包。

要确认,请输入y并按 Enter。

这会以交互模式生成插件的 PHP、SCSS 和 JS 文件。

以下是我们将在示例中使用的详细信息。 随意根据您的喜好更改这些详细信息:


点击回车后,它会下载并配置插件。

安装块插件。
安装块插件。

该过程可能需要几分钟。 完成后,您应该会看到以下屏幕:

块在插件文件夹中引导。
块在插件文件夹中引导。

您将看到可以从插件目录中运行的命令列表:

  • $ npm start – 开始构建以进行开发。
  • $ npm run build – 构建生产代码。
  • $ npm run format – 格式化文件。
  • $ npm run lint:css – Lint CSS 文件。
  • $ npm run lint:js – Lint JavaScript 文件。
  • $ npm run packages-update – 将 WordPress 包更新到最新版本。

好的,现在使用以下命令移动到插件目录:

 cd author-plugin

并开始您的开发构建:

 npm start

接下来,导航到 WordPress 仪表板中的插件屏幕并激活作者框插件:

块插件列在插件屏幕中。
块插件列在插件屏幕中。

现在您可以检查插件是否正常工作。 创建一个新帖子并开始输入/以启动快速插入器:

快速插入器中的块项目。
快速插入器中的块项目。

您还可以在小部件类别下的块插入器中找到作者框块。 选择块以将其添加到编辑器画布:

WordPress 块插入器。
WordPress 块插入器

你完成了。 现在保存帖子并预览页面以检查块是否正确显示。

块脚手架

我们在上一篇文章中介绍了块脚手架。 因此,在这里我们将只提供我们将为我们的示例修改的文件的快速概述。

根文件夹
根文件夹是您可以找到主要 PHP 文件和几个子文件夹的地方。

作者-plugin.php
默认情况下, @wordpress/create-block包提供以下 PHP 文件:

 /** * Plugin Name: Author box * Description: An example block for Kinsta readers * Requires at least: 5.8 * Requires PHP: 7.0 * Version: 0.1.0 * Author: Carlo * License: GPL-2.0-or-later * License URI: https://www.gnu.org/licenses/gpl-2.0.html * Text Domain: author-plugin * * @package author-box */ /** * Registers the block using the metadata loaded from the `block.json` file. * Behind the scenes, it registers also all assets so they can be enqueued * through the block editor in the corresponding context. * * @see https://developer.wordpress.org/reference/functions/register_block_type/ */ function author_box_author_plugin_block_init() { register_block_type( __DIR__ . '/build' ); } add_action( 'init', 'author_box_author_plugin_block_init' );

在标题中,您会注意到我们在设置时输入的详细信息。

使用静态块,大部分时间您将处理位于src文件夹中的 JavaScript 文件。 使用动态块,您将编写 PHP 代码以在前端显示块内容。

src文件夹
src文件夹是您的开发文件夹。 在这里,您将找到以下文件:

  • 块.json
  • index.js
  • 编辑.js
  • 保存.js
  • 编辑器.scss
  • 样式.scss

块.json
block.json是您的元数据文件。 @wordpress/create-block生成以下block.json文件:

 { "$schema": "https://schemas.wp.org/trunk/block.json", "apiVersion": 2, "name": "author-box/author-plugin", "version": "0.1.0", "title": "Author box", "category": "widgets", "icon": "businessperson", "description": "An example block for Kinsta readers", "supports": { "html": false }, "textdomain": "author-plugin", "editorScript": "file:./index.js", "editorStyle": "file:./index.css", "style": "file:./style-index.css" }

要更详细地了解block.json文件,请参阅我们之前的博客文章。

index.js
index.js文件是您在客户端注册块类型的地方:

 import { registerBlockType } from '@wordpress/blocks'; import './style.scss'; import Edit from './edit'; import save from './save'; registerBlockType('author-box/author-plugin', { edit: Edit, save, });

编辑.js
edit.js文件是您构建在编辑器中呈现的块界面的位置:

 import { __ } from '@wordpress/i18n'; import { useBlockProps } from '@wordpress/block-editor'; import './editor.scss'; export default function Edit() { return ( <p {...useBlockProps()}> {__('Author box – hello from the editor!', 'author-plugin')} </p> ); }

保存.js
save.js文件包含构建要保存到数据库中的块内容的脚本。 我们不会在本教程中使用此文件:

 import { __ } from '@wordpress/i18n'; import { useBlockProps } from '@wordpress/block-editor'; export default function save() { return ( <p {...useBlockProps.save()}> {__('Author box – hello from the saved content!', 'author-plugin')} </p> ); }

在编辑器中构建要渲染的块

在 Visual Studio Code 或您喜欢的任何代码编辑器中打开您的项目。

如果您使用的是 Visual Studio Code,请转到Terminal -> New Terminal 。 这将在项目的根文件夹上启动一个终端窗口。

在终端(或您最喜欢的命令行工具)中,输入以下命令:

 npm start

您现在正在开发模式下运行节点环境。

Visual Studio Code 中的块插件项目。
Visual Studio Code 中的块插件项目。

从这里开始,您将遵循两条不同的路线。 要在编辑器中呈现块,您将在edit.js文件中工作。 要在前端渲染块,您需要在主插件文件中编写 PHP 代码。

现在卷起袖子,因为编码开始了:

在服务器上注册块

首先,您必须在服务器上注册块并编写 PHP 代码以从数据库中检索数据。

author-plugin.php文件中,您需要将第二个参数传递给register_block_type函数:

 function author_box_author_plugin_block_init() { register_block_type( __DIR__ . '/build', array( 'render_callback' => 'author_box_author_plugin_render_author_content' ) ); } add_action( 'init', 'author_box_author_plugin_block_init' );

第二个参数是用于注册块类型的参数数组(请参阅此处的可用参数的完整列表)。 在上面的代码中,我们只提供了render_callback ,它决定了在屏幕上渲染块的回调函数。

接下来,您将声明该函数:

 function author_box_author_plugin_render_author_content() { return 'Hello World!'; }

保存文件,创建新帖子或页面,并将作者框块添加到编辑器画布。

WordPress 块插入器。
WordPress 块插入器。

块编辑器仍然显示起始块,因为我们还没有更改edit.js文件。

但是如果你在前端预览帖子,你会看到原来的块内容现在已经被“Hello World”字符串替换了。

现在,由于前端呈现的 HTML 是由 PHP 文件生成的,因此save函数不需要返回任何内容。 所以我们直接进入save.js文件,修改代码如下:

 export default function save() { return null; }

定义块属性

现在您需要一个地方来存储用户设置。 例如,要从数据库中检索的帖子数量、是否显示指定字段等。为此,您将在block.json文件中定义许多attributes

例如,您可以让用户确定要包含在块中的帖子数量、显示特色图片、日期、摘录和/或隐藏/显示作者个人资料图片的选项。

以下是我们将用于构建示例块的完整属性列表:

 { ... "attributes": { "numberOfItems": { "type": "number", "default": 3 }, "columns": { "type": "number", "default": 1 }, "displayDate": { "type": "boolean", "default": true }, "displayExcerpt": { "type": "boolean", "default": true }, "displayThumbnail": { "type": "boolean", "default": true }, "displayAuthorInfo": { "type": "boolean", "default": true }, "showAvatar": { "type": "boolean", "default": true }, "avatarSize": { "type": "number", "default": 48 }, "showBio": { "type": "boolean", "default": true } } }

构建要在编辑器中渲染的块

getEntityRecords选择器包含在@wordpress/data包中。 要使用它,你需要从你的edit.js文件中的那个包中导入useSelect钩子:

 import { useSelect } from '@wordpress/data';

接下来,将以下代码添加到Edit()函数中:

 const posts = useSelect( ( select ) => { return select( 'core' ).getEntityRecords( 'postType', 'post', { 'per_page': 3 }); });

在上面的代码中,我们硬编码了帖子的数量。 但是您可能希望让用户能够设置不同数量的帖子。 您可以为此使用属性。

在您的block.json 中,您应该已经定义了numberOfItems属性。 您可以在Edit功能中使用它,如下所示:

 export default function Edit( { attributes } ) { const { numberOfItems } = attributes; const posts = useSelect( ( select ) => { return select( 'core' ).getEntityRecords( 'postType', 'post', { 'per_page': numberOfItems }); }); console.log( posts ); return ( ... ); }

您还不会在屏幕上看到这些帖子,但运行一个console.log并查看浏览器检查器的控制台中会发生什么:

浏览器控制台中的结果。
浏览器控制台中的结果。

useSelect可能有两个参数:一个内联回调和一个依赖数组。 两者都返回回调的记忆版本,仅当其中一个依赖项发生更改时才会更改。

因此,为了在每个numberOfItems属性更改时重新获取帖子,您必须更改Edit函数,如下所示:

 export default function Edit( { attributes } ) { const { numberOfItems } = attributes; const posts = useSelect( ( select ) => { return select( 'core' ).getEntityRecords( 'postType', 'post', { 'per_page': numberOfItems }); }, [ numberOfItems ] ); console.log(posts); return ( ... ); }

接下来,您必须呈现您的帖子列表。 为此,您可以使用内置的 JavaScript map方法:

 export default function Edit( { attributes } ) { const { numberOfItems } = attributes; const posts = useSelect( ( select ) => { return select( 'core' ).getEntityRecords( 'postType', 'post', { 'per_page': numberOfItems }); }, [ numberOfItems ] ); console.log(posts); return ( <div { ...useBlockProps() }> <ul> { posts && posts.map( ( post ) => { return ( <li key={ post.id }> <h5> <a href={ post.link }> { post.title.rendered ? post.title.rendered : __( 'Default title', 'author-plugin' ) } </a> </h5> </li> ) })} </ul> </div> ); }

首先,它检查数组中是否至少有一个帖子,然后运行循环。

请注意,当我们使用带有 React 组件的map方法时,我们还使用key属性将帖子 ID 分配给当前列表项。

post.linkpost.title.rendered分别呈现帖子 URL 和标题。

下图显示了post对象属性的完整列表。

Post 对象。
Post 对象。

上面的代码只是getEntityRecords用法的一个基本示例。 现在是时候将我们的知识付诸实践了。

假设您希望阻止您的块呈现用户可能已添加到帖子标题中的 HTML 标记。 WordPress 为此提供了一个RawHTML组件。

首先,您将从 @wordpress/element 包中导入组件:

 import { RawHTML } from '@wordpress/element';

接下来,您将在RawHTML元素中包装文章标题:

 <div { ...useBlockProps() }> <ul> { posts && posts.map((post) => { return ( <li key={ post.id }> <h5> <a href={ post.link }> { post.title.rendered ? ( <RawHTML> { post.title.rendered } </RawHTML> ) : ( __( 'Default title', 'author-plugin' ) )} </a> </h5> </li> ) })} </ul> </div>

就是这样。 现在将 HTML 标签添加到您的帖子标题并保存帖子。 然后在使用和不RawHTML的情况下测试您的代码,并查看您的块的内容在屏幕上的变化情况。

添加日期

WordPress 提供了许多 JavaScript 函数来管理和格式化日期。 要使用这些函数,您首先需要从edit.js文件中的@wordpress/date包中导入它们:

 import { dateI18n, format, __experimentalGetSettings } from '@wordpress/date';
  • dateI18n :格式化日期,将其转换为站点的语言环境。
  • format :格式化日期。
  • __experimentalGetSettings :以 WordPress 常规设置中设置的格式显示日期。

这些函数没有记录,但您会在几个块的源代码中找到有用的示例。 例如,请参阅 latest-posts 和 post-date edit.js文件。

现在添加displayDate属性:

 const { numberOfItems, displayDate } = attributes;

然后在<li>元素中添加以下代码:

 { displayDate && ( <time className='wp-block-author-box-author-plugin__post-date' dateTime={ format( 'c', post.date_gmt ) } > { dateI18n( __experimentalGetSettings().formats.date, post.date_gmt )} </time> ) }

这里会发生什么?

  • 如果displayDatetrue ,则使用time元素显示日期。
  • dateTime属性以一种允许的格式提供元素的时间和/或日期。
  • dateI18n以本地化格式检索日期。 此函数的工作方式类似于 PHPPHP date_i18n WordPress 函数。

添加摘录

现在应该很容易添加帖子摘录。 首先,看一下浏览器检查器中的excerpt属性。 您会看到实际内容存储在excerpt.rendered中。

检查 Chrome DevTools 中的帖子摘录。
检查 Chrome DevTools 中的帖子摘录。

接下来,将displayExcerpt属性添加到attributes对象:

 const { numberOfItems, displayDate, displayExcerpt } = attributes;

然后在Edit函数中的</li>结束标记之前添加以下代码:

 { displayExcerpt && post.excerpt.rendered && ( <p> <RawHTML> { post.excerpt.rendered } </RawHTML> </p> ) }

如果您不熟悉 JavaScript,这里和上面我们使用了短路评估,如果所有条件都为真,则返回最后一个操作数的值(在 Inline If with Logical && Operator and Logical AND (&& ))。

最后,您可以再次测试您的代码。 更改block.json文件中的属性值,看看在编辑器中会发生什么。

添加特色图片

现在您需要添加呈现特色图像的代码。 开始将displayThumbnail属性添加到attributes

 const { numberOfItems, displayDate, displayExcerpt, displayThumbnail } = attributes;

现在您需要弄清楚特色图像的存储位置。 正如我们上面提到的,要获得特色图像,您需要在查询中添加一个新的_embed参数。 回到您的代码,更改查询参数,如下所示:

const posts = useSelect( ( select ) => { return select( 'core' ).getEntityRecords( 'postType', 'post', { 'per_page': numberOfItems, '_embed': true }); }, [ numberOfItems ] );

在这里,我们简单地将'_embed': true添加到参数数组中。 这提供了一个包含_embedded属性的post对象,该属性提供了显示特色图像所需的图像详细信息。

现在您应该知道在哪里可以找到图像详细信息。

getEntityRecords 响应中的特色图像详细信息。
getEntityRecords 响应中的特色图像详细信息。

您只需要添加在屏幕上呈现图像的代码:

 { displayThumbnail && post._embedded && post._embedded['wp:featuredmedia'] && post._embedded['wp:featuredmedia'][0] && <img className='wp-block-author-box-author-plugin__post-thumbnail' src={ post._embedded['wp:featuredmedia'][0].media_details.sizes.medium.source_url } alt={ post._embedded['wp:featuredmedia'][0].alt_text } /> }

保存文件,切换到块编辑器,检查displayThumbnail属性设置为true时图像是否正确显示。

带有特色图片、日期和摘录的帖子列表。
带有特色图片、日期和摘录的帖子列表。

添加侧边栏控件

到目前为止,我们一直在使用block.json中设置的属性默认值。 但是从我们之前的文章中我们知道,我们可以定义事件处理程序,让用户能够为每个属性分配自定义值。

为此,您将向块设置侧边栏添加一组控件。 在edit.js中,从相应的包中导入以下组件:

 import { useBlockProps, InspectorControls } from '@wordpress/block-editor'; import { PanelBody, PanelRow, QueryControls, ToggleControl, RangeControl } from '@wordpress/components';
  • InspectorControls :包含影响整个块的侧边栏设置(参见 GitHub)
  • PanelBody :将可折叠容器添加到设置侧边栏(参见 GitHub)
  • PanelRow :为侧边栏控件生成一个通用容器(参见 GitHub)
  • QueryControls :提供设置控件来构建查询(参见 GitHub)
  • ToggleControl :为用户提供一个切换按钮来启用/禁用特定选项(参见 GitHub)
  • RangeControl :用于从一系列增量值中进行选择(参见 GitHub)

接下来,您需要更新Edit功能以使用现在可用的控件。 首先,修改Edit函数如下:

 export default function Edit( { attributes, setAttributes } ) { const { numberOfItems, columns, displayExcerpt, displayDate, displayThumbnail } = attributes; const posts = useSelect( ( select ) => { return select( 'core' ).getEntityRecords( 'postType', 'post', { 'per_page': numberOfItems, '_embed': true }); }, [ numberOfItems ] ); ... }

请注意传递给Edit函数的setAttributes属性。

现在您可以将相应的元素添加到您的 JSX 代码中:

 return ( <> <InspectorControls> <PanelBody title={ __( 'Content Settings', 'author-plugin' ) }> <PanelRow> <QueryControls numberOfItems={ numberOfItems } onNumberOfItemsChange={ ( value ) => setAttributes( { numberOfItems: value } ) } minItems={ 1 } maxItems={ 10 } /> </PanelRow> <PanelRow> <RangeControl label={ __( 'Number of Columns', 'author-plugin' ) } value={ columns } onChange={ ( value ) => setAttributes( { columns: value } ) } min={ 1 } max={ 4 } required /> </PanelRow> <PanelRow> <ToggleControl label={ __( 'Show Featured Image', 'author-plugin' ) } checked={ displayThumbnail } onChange={ () => setAttributes( { displayThumbnail: ! displayThumbnail } ) } /> </PanelRow> <PanelRow> <ToggleControl label={ __( 'Show Date', 'author-plugin' ) } checked={ displayDate } onChange={ () => setAttributes( { displayDate: ! displayDate } ) } /> </PanelRow> <PanelRow> <ToggleControl label={ __( 'Display Excerpt', 'author-plugin' ) } checked={ displayExcerpt } onChange={ () => setAttributes( { displayExcerpt: ! displayExcerpt } ) } /> </PanelRow> </PanelBody> </InspectorControls> <div { ...useBlockProps() }> ... </div> </> );

哇,这么多代码,不是吗? 但这很容易理解。

这里最值得关注的元素属性是QueryControls中的onNumberOfItemsChangeRangeControlToggleControl中的onChange 。 这些属性设置了使用户能够自定义块的外观和/或行为所需的事件处理程序。

你还会注意到我们使用了<></>标签,它们是声明 React 片段的简短语法。

现在,保存您的文件,跳到编辑器中,然后刷新页面:

块设置。
块设置。

里面都有吗? 然后让我们继续添加帖子作者的详细信息。

查找帖子作者

正如我们上面提到的,我们的块将显示由与当前帖子相同的作者撰写的文章列表。

要获取帖子作者的 ID,您将从core/editor数据存储区导入getCurrentPostAttribute选择器:

 wp.data.select( 'core/editor' ).getCurrentPostAttribute( 'author' )

getCurrentPostAttribute返回已保存帖子的属性值。

获得作者 ID 后,您可以更改查询,如下所示:

 const posts = useSelect( ( select ) => { const _authorId = select( 'core/editor' ).getCurrentPostAttribute( 'author' ); return select( 'core' ).getEntityRecords( 'postType', 'post', { 'author': _authorId, 'per_page': numberOfItems, '_embed': true }); }, [ numberOfItems ] );

使用此代码,您将获得与当前帖子相同作者的n篇文章的列表。

现在您有了作者 ID,您还可以使用它从数据库中获取其他数据。

显示作者详细信息

由于我们没有任何可用的文档,我们使用核心 Post Author 块中的代码作为参考。

要显示作者详细信息,您首先需要导入一个新的依赖项:

 import { forEach } from 'lodash';

然后,在Edit函数中,按如下方式更新attributes对象:

 const { numberOfItems, columns, displayExcerpt, displayDate, displayThumbnail, displayAuthorInfo, showAvatar, avatarSize, showBio } = attributes;

完成后,您将编辑上一节中看到的代码以检索作者详细信息:

 const { authorDetails, posts } = useSelect( ( select ) => { const _authorId = select( 'core/editor' ).getCurrentPostAttribute( 'author' ); const authorDetails = _authorId ? select( 'core' ).getUser( _authorId ) : null; const posts = select( 'core' ).getEntityRecords( 'postType', 'post', { 'author': _authorId, 'per_page': numberOfItems, '_embed': true }); return { authorDetails: authorDetails, posts: posts }; }, [ numberOfItems ] );

请注意,我们使用getUser选择器来获取作者详细信息。

接下来,您可能想要获取作者的头像。 下面的代码构建了一个存储头像 URL 和大小的项目数组:

 const avatarSizes = []; if ( authorDetails ) { forEach( authorDetails.avatar_urls, ( url, size ) => { avatarSizes.push( { value: size, label: `${ size } x ${ size }`, } ); } ); }

然后,您将添加侧边栏面板和控件,以使用户能够自定义块中的作者区域:

 return ( <> <InspectorControls> <PanelBody title={ __( 'Author Info', 'author-plugin' ) }> <PanelRow> <ToggleControl label={ __( 'Display Author Info', 'author-plugin' ) } checked={ displayAuthorInfo } onChange={ () => setAttributes( { displayAuthorInfo: ! displayAuthorInfo } ) } /> </PanelRow> { displayAuthorInfo && ( <> <PanelRow> <ToggleControl label={ __( 'Show avatar' ) } checked={ showAvatar } onChange={ () => setAttributes( { showAvatar: ! showAvatar } ) } /> { showAvatar && ( <SelectControl label={ __( 'Avatar size' ) } value={ avatarSize } options={ avatarSizes } onChange={ ( size ) => { setAttributes( { avatarSize: Number( size ), } ); } } /> ) } </PanelRow> <PanelRow> <ToggleControl label={ __( 'Show Bio', 'author-plugin' ) } checked={ showBio } onChange={ () => setAttributes( { showBio: ! showBio } ) } /> </PanelRow> </> ) } </PanelBody> ... </InspectorControls> ... </> );

下图显示了更新的设置侧边栏:

作者信息设置面板。
作者信息设置面板。

最后,您可以将作者部分添加到您的块中:

 return ( <> <InspectorControls> ... </InspectorControls> <div { ...useBlockProps() }> { displayAuthorInfo && authorDetails && ( <div className="wp-block-author-box-author-plugin__author"> { showAvatar && ( <div className="wp-block-author-box-author-plugin__avatar"> <img width={ avatarSize } src={ authorDetails.avatar_urls[ avatarSize ] } alt={ authorDetails.name } /> </div> ) } <div className='wp-block-author-box-author-plugin__author-content'> <p className='wp-block-author-box-author-plugin__name'> { authorDetails.name } </p> { showBio && // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining authorDetails?.description && authorDetails.description.length > 0 && ( <p className='wp-block-author-box-author-plugin__description'>{ authorDetails.description }</p> ) } </div> </div> )} <ul> ... </ul> </div> </> );

下图显示了它在屏幕上的呈现方式。

作者详细信息部分和信息设置。
作者详细信息部分和信息设置。

现在保存您的edit.js文件并运行您的测试。 您的块应包含不同的元素,具体取决于块设置。

作者详细信息未显示作者的简历。
作者详细信息未显示作者的简历。

最后一件事仍然缺失:显示文章的列数。

更改列数

为了让用户能够在列中显示文章预览,我们在block.json文件中定义了columns属性。 我们还在脚本中包含了一个columns属性,并创建了一个设置控件以允许用户更改列数,尽管此更改目前没有效果。

在上面的 JSX 代码中,您应该已经注意到我们为几个元素添加了 CSS 类:

分配给作者部分中元素的类:

  • wp-block-author-box-author-plugin__author
  • wp-block-author-box-author-plugin__avatar
  • wp-block-author-box-author-plugin__author-content
  • wp-block-author-box-author-plugin__name
  • wp-block-author-box-author-plugin__description

分配给内容部分元素的类:

  • wp-block-author-box-author-plugin__post-items
  • wp-block-author-box-author-plugin__post-thumbnail
  • wp-block-author-box-author-plugin__post-title
  • wp-block-author-box-author-plugin__post-date
  • wp-block-author-box-author-plugin__post-excerpt

还缺一节课。 该类的名称将动态生成,以反映用户设置的列数。

回到Edit.js文件,修改ul元素如下:

 <ul className={ `wp-block-author-box-author-plugin__post-items columns-${ columns }` }> ... </ul>

我们根据模板文字语法添加了一个新的columns-${ columns }类,以在字符串中插入表达式。 这样,附加到ul元素的属性将取决于用户设置(例如columns-1columns-2等)。

现在打开style.scss文件并将现有代码替换为以下内容:

 .wp-block-author-box-author-plugin { background-color: #21759b; color: #fff; padding: .6em; ul.wp-block-author-box-author-plugin__post-items { padding: 0; list-style-type: none; display: grid; gap: .5em; @for $i from 2 through 4 { &.columns-#{ $i } { grid-template-columns: repeat(#{ $i }, 1fr); } } li { list-style: none; img.wp-block-author-box-author-plugin__post-thumbnail { height: auto; max-width: 100%; } } } } .wp-block-author-box-author-plugin__author { display: flex; flex-wrap: wrap; } .wp-block-author-box-author-plugin__avatar { margin-right: 1em; } .wp-block-author-box-author-plugin__author-content { flex-basis: 0; flex-grow: 1; }

我们不会深入研究该代码,超出了本文的范围。 但是,如果您想深入了解,可以参考以下资源:

  • CSS 网格布局
  • 学习 CSS 网格
  • Sass 中的@for 规则
  • 嵌套在 Sass 中
编辑器中的作者块。
编辑器中的作者块。

这就是在编辑器中渲染块的过程。

构建要在页面上呈现的块

现在在编辑器中渲染块的代码已经完成,我们可以继续构建块以在前端渲染。

正如我们前面提到的,当涉及到动态块时,插件文件负责生成要在前端呈现的 HTML。

因此,打开插件的主文件(在我们的示例中为author-plugin.php )。

首先要做的是使块属性可用于 WordPress PHP 函数。 在您的 PHP 文件中,按如下方式更改函数定义:

 function author_box_author_plugin_render_author_content( $attr ) { ... }

现在您可以使用 WordPress 函数来检索和操作数据。 例如,您可以使用get_posts检索最新的博客文章(在我们涵盖get_posts函数的深入文章中阅读更多内容):

 function author_box_author_plugin_render_author_content( $attr ) { $args = array( 'numberposts' => $attr['numberOfItems'], ); $my_posts = get_posts( $args ); if( ! empty( $my_posts ) ){ $output = '<ul>'; foreach ( $my_posts as $p ){ $output .= '<li><a href="' . esc_url( get_permalink( $p->ID ) ) . '">' . $p->post_title . '</a></li>'; } $output .= '</ul>'; } return $output ?? '<strong>Sorry. No posts matching your criteria!</strong>'; }

上面的函数从您的 WordPress 数据库中检索最新的numberOfItems博客文章(默认情况下post_type设置为post )并返回一个$post对象数组。 然后它遍历数组以构建列表项。

如果您检查 HTML 输出,您会注意到它是一个简单的帖子列表,如下图所示:

一个简单的帖子列表。
一个简单的帖子列表。

在我们之前的文章中,我们提到您将使用useBlockProps React 钩子在 JSX 代码中标记块的包装器元素。 您需要在 PHP 函数中执行相同的操作。

WordPress 为此提供了get_block_wrapper_attributes函数。

因此,更改您的 PHP 代码如下:

 function author_box_author_plugin_render_author_content( $attr ) { $args = array( 'numberposts' => $attr['numberOfItems'] ); $my_posts = get_posts( $args ); if( ! empty( $my_posts ) ){ $output = '<div ' . get_block_wrapper_attributes() . '>'; $output .= '<ul>'; foreach ( $my_posts as $p ){ $title = $p->post_title ? $p->post_title : 'Default title'; $url = esc_url( get_permalink( $p->ID ) ); $output .= '<li>'; $output .= '<a href="' . $url . '">' . $title . '</a>'; $output .= '</li>'; } $output .= '</ul>'; $output .= '</div>'; } return $output ?? '<strong>Sorry. No posts matching your criteria!</strong>'; }

现在已经为容器元素分配了一个wp-block-author-box-author-plugin类,并且该块具有不同的背景颜色。

然后get_posts函数获取WP_Posts数据, foreach循环构建列表项(另请参见如何显示 get_posts 返回的数据)。

分配了 CSS 类的帖子列表。
分配了 CSS 类的帖子列表。

添加特色图片、日期和摘录

接下来,您需要添加帖子缩略图、日期和摘录。 在同一个文件中,更改您的 PHP 代码,如下所示:

 function author_box_author_plugin_render_author_content( $attr ) { $args = array( 'numberposts' => $attr['numberOfItems'] ); $my_posts = get_posts( $args ); if( ! empty( $my_posts ) ){ $output = '<div ' . get_block_wrapper_attributes() . '>'; $output .= '<ul class="wp-block-author-box-author-plugin__post-items columns-">'; foreach ( $my_posts as $p ){ $title = $p->post_title ? $p->post_title : 'Default title'; $url = esc_url( get_permalink( $p->ID ) ); $thumbnail = has_post_thumbnail( $p->ID ) ? get_the_post_thumbnail( $p->ID, 'medium' ) : ''; $output .= '<li>'; if( ! empty( $thumbnail ) && $attr['displayThumbnail'] ){ $output .= $thumbnail; } $output .= '<h5><a href="' . $url . '">' . $title . '</a></h5>'; if( $attr['displayDate'] ){ $output .= '<time datetime="' . esc_attr( get_the_date( 'c', $p ) ) . '">' . esc_html( get_the_date( '', $p ) ) . '</time>'; } if( get_the_excerpt( $p ) && $attr['displayExcerpt'] ){ $output .= '<p>' . get_the_excerpt( $p ) . '</p>'; } $output .= '</li>'; } $output .= '</ul>'; $output .= '</div>'; } return $output ?? '<strong>Sorry. No posts matching your criteria!</strong>'; }

foreach循环遍历$my_posts数组。 在每次迭代中,有几个条件检查属性值并相应地构建输出。

现在看看屏幕上的输出:

带有特色图片、日期和摘录的帖子列表。
带有特色图片、日期和摘录的帖子列表。

现在你可以运行你的测试了。 更改日期、摘录和缩略图设置,并在前端检查块内容的变化情况。

在列中显示帖子

在我们的 JavaScript 代码中,我们使用了columns-${ columns }类在列中显示帖子预览。 现在我们需要在 PHP 中做同样的事情。

为此,您只需添加这两行代码:

 $num_cols = $attr['columns'] > 1 ? strval( $attr['columns'] ) : '1'; $output .= '<ul class="wp-block-author-box-author-plugin__post-items columns-' . $num_cols . '">';

这会将columns-n类附加到包含帖子预览的ul元素。 现在页面上显示的列数应该与块设置中设置的列数匹配。

建立作者框

最后,您需要构建包含作者详细信息的框,包括头像、姓名和描述。

在回调函数中,您需要添加一组条件来检查每个属性的当前值:

 if( $attr['displayAuthorInfo'] ){ $output .= '<div class="wp-block-author-box-author-plugin__author">'; if( $attr['showAvatar'] ){ $output .= '<div class="wp-block-author-box-author-plugin__avatar">' . get_avatar( get_the_author_meta( 'ID' ), $attr['avatarSize'] ) . '</div>'; } $output .= '<div class="wp-block-author-box-author-plugin__author-content">'; $output .= '<div class="wp-block-author-box-author-plugin__name">' . get_the_author_meta( 'display_name' ) . '</div>'; if( $attr['showBio'] ){ $output .= '<div class="wp-block-author-box-author-plugin__description">' . get_the_author_meta( 'description' ) . '</div>'; } $output .= '</div>'; $output .= '</div>'; }

代码非常简单。 它检查每个属性的当前值,如果为true ,则生成必要的 HTML。

现在保存您的 PHP 文件并将编辑器中的块与前端的相同块进行比较。

我们在块编辑器中的自定义块。
我们在块编辑器中的自定义块。

您可以在这个公共 Gist 中找到示例块的完整代码。

动态块是类固醇下的古腾堡块,本指南涵盖了为您的下一个项目创建动态块所需了解的所有内容点击鸣叫

动态块开发的推荐资源

如果您在阅读本文时竖起耳朵并开始认识到学习如何创建古腾堡区块所带来的专业发展机会,那么我们的建议是继续探索和获得区块开发背后技术的新技能。

尽管仍然缺少可靠的官方文档,但仍然有很好的资源,包括免费和付费的,我们在撰写本文时进行了咨询。 在众多可用资源中,我们推荐以下资源:

官方资源

  • 数据
  • 核心数据
  • 创建动态块
  • 古腾堡区块开发简介
  • MeetUp 上的 WordPress 社交学习

来自 WordPress 核心贡献者的推荐教程

  • Ryan Welcher (@ryanwelcher) 使用 getEntityRecords 在 Gutenberg 请求数据
  • Darren Ethier (@nerrad) 的 @wordpress/data API 实用概述

JavaScript、React 和 Redux 资源

  • MDN 的 JavaScript 教程
  • React 入门(官方)
  • Redux 教程(官方)

Kinsta的相关资源

  • 什么是 JavaScript? 看看网络上最流行的脚本语言
  • 处理 JavaScript 错误的权威指南
  • 什么是 Node.js 以及为什么要使用它
  • 如何在 Windows、macOS 和 Linux 上安装 Node.js 和 npm
  • 如何使用多种工具调试 Node.js 代码
  • Node.js 与 PHP:正面对比
  • 2022 年 10 种最受欢迎​​的 Node.js 应用程序类型
  • Angular vs React:详细的并排比较

概括

通过古腾堡区块开发,我们已经到达了这个(第二次)漫长旅程的终点​​。

在本文中,我们介绍了一些高级主题,例如应用程序状态和 Redux 存储。 但希望您现在应该对块开发有更好的了解。

是的,在构建高级 Gutenberg 块时,Node.js、Webpack、Babel、React 和 Redux 技能是必不可少的,但您无需成为 React 忍者即可开始。 学习如何开发古腾堡积木并不一定很复杂。 只需以正确的动机并遵循适当的学习路径即可。

我们希望这篇文章 - 以及上一篇文章 - 为您提供正确的地图,以找到您的道路并立即开始 Gutenberg 开发。

现在由你决定! 你创建动态块了吗? 你有什么例子可以和我们分享吗? 在你的经历中,最大的障碍是什么? 随意在下面发表评论。