如何为古腾堡创建动态区块

如何为古腾堡创建动态区块

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

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

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

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

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

  1. 什么是动态区块?一个例子
  2. 从开发人员的角度来看Gutenberg动态区块
  3. 如何访问文章数据
  4. 如何创建动态区块:示例项目
  5. 在编辑器中构建要渲染的区块
  6. 构建要在页面上呈现的区块
  7. 动态区块开发的推荐资源

什么是动态区块?一个例子

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

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

包括文章作者和最新文章组区块

包括文章作者和最新文章组区块

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

  • 标题核心区块
  • 文章作者核心区块
  • 最新文章核心区块

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

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

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

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

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

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

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

动态区块有两个主要用例。

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

添加查询循环区块

添加查询循环区块

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

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

意外或无效的内容

意外或无效的内容

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

  1. 应用程序状态和数据存储
  2. Gutenberg中的模块化、包和数据存储
  3. WordPress数据存储

应用程序状态和数据存储

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  • 生产包组成了在浏览器中运行的生产代码。WordPress中有两种类型的生产包:
  • 开发包用于开发模式。这些包包括用于linting、测试、构建等的工具。

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

WordPress数据存储

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

Info Redux是JavaScript应用程序的状态管理器。Redux的工作方式总结为三个基本原则

  • 单一事实来源:应用程序的全局状态存储在单个存储中的对象树中。
  • 状态是只读的:改变状态的唯一方法是发出一个动作,一个描述发生了什么的对象。
  • 使用纯函数进行更改:要指定状态树如何通过操作进行转换,您可以编写纯reducer。

官方文档提供了以下定义

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

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

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

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

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

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

wp.data.select("core")

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

核心WordPress数据存储对象

核心WordPress数据存储对象

Info selectors对象包括一组用于访问和导出状态值的函数。选择器是一个接受状态和可选参数并从状态返回一些值的函数。调用选择器是从您的状态中检索数据的主要机制,并作为对原始数据的有用抽象,这些原始数据通常更容易更改并且不太容易用作规范化对象。(来源:Github

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函数,它是提供对post数据的访问权限的选择器。

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

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

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

getEntityRecords返回文章列表

getEntityRecords返回文章列表

Info 请注意,第一次将请求发送到Rest API时,响应将一直持续为null到请求完成。所以,如果你得到null了,不用担心,然后再试一次。

getEntityRecords接受三个参数

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

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

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

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选择器的详细视图,另请参阅使用getEntityRecords在Gutenberg中请求数据

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

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

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

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

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

设置JavaScript开发环境

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

安装或更新Node.js

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

node -v

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

设置您的开发环境

接下来,您需要一个WordPress开发环境。

在开发环境中创建自定义站点

在开发环境中创建自定义站点

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

设置你的块插件

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

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

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

Mac OS文件夹中的新终端

Mac OS文件夹中的新终端

在那里,运行以下命令:

npx @wordpress/create-block

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

安装@wordpress/create-block package

安装@wordpress/create-block package

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

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

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

  • 用于识别的区块的slug(也是插件和输出文件夹名称):author-plugin
  • 区块名称的内部命名空间(对您的产品来说是唯一的):author-box
  • 区块的显示标题:Author box
  • 您的区块的简短描述(可选):An example block for readers
  • 破折号使您更容易识别您的区块(可选):businessperson
  • 帮助用户浏览和发现您的区块的类别名称:widgets
  • 插件作者的名字(可选)。可以使用逗号列出多个作者:your name
  • 插件许可证的简称(可选):
  • 许可证全文的链接(可选):
  • 插件当前版本号:0.1.0

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

安装区块插件

安装区块插件

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

区块在插件文件夹中引导

区块在插件文件夹中引导

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

  • $ 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仪表盘中的插件屏幕并启用Author box插件:

区块插件列在插件管理页面中

区块插件列在插件管理页面中

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

快速插入器中的区块项目

快速插入器中的区块项目

您还可以在Widgets类别下的块插入器中找到Author box区块。选择区块以将其添加到编辑器画布:

WordPress区块插入器

WordPress区块插入器

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

区块脚手架

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

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

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

/**
* Plugin Name:       Author box
* Description:       An example block for 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文件夹是您的开发文件夹。在这里,您将找到以下文件:

  • block.json
  • index.js
  • edit.js
  • save.js
  • editor.scss
  • style.scss

block.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,
});

edit.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>
);
}

save.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代码。

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

  1. 在服务器上注册区块
  2. 定义区块属性
  3. 构建要在编辑器中渲染的区块
  4. 添加日期
  5. 添加摘录
  6. 添加特色图片
  7. 添加侧边栏控件
  8. 查找文章作者
  9. 显示作者详细信息
  10. 更改列数

Info 在本文中,我们仅提供代码片段。完整代码可在Gist上找到

在服务器上注册区块

首先,您必须在服务器上注册块并编写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;
}

定义区块属性

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

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

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

{
...
"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包中。要使用它,您需要useSelect从文件中的该包中导入钩子edit.js

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

Info useSelect是一个自定义的反应钩子,用于从基于useCallbackReact钩子的注册选择器中检索值。

接下来,将以下代码添加到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 (
...
);
}

接下来,您必须呈现您的文章列表。为此,您可以使用内置的JavaScriptmap方法

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>
);
}

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

Info map()方法创建一个新数组,其中填充了在调用数组中的每个元素上调用提供的函数的结果——来源:MDN web docs

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

Info “key”是创建元素列表时需要包含的特殊字符串属性 – 来源: React Docs中的列表和键

post.linkpost.title.rendered分别呈现文章URL和标题。

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

Post对象

Post对象

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

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

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

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 package 导入它们:

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

这些函数没有记录,但您会在几个块的源代码中找到有用的示例。例如,请参阅latest-postspost-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以本地化格式检索日期。该函数的工作方式类似于PHP date_i18nWordPress函数

添加摘录

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

检查Chrome DevTools中的文章摘录

检查Chrome DevTools中的文章摘录

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

const { numberOfItems, displayDate, displayExcerpt } = attributes;

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

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

如果你不熟悉JavaScript,这里和上面我们使用了Short Circuit Evaluation,如果所有条件都为真,则返回最后一个操作数的值(阅读更多Inline If with Logical && OperatorLogical AND (&& ))。

Info 在JavaScript中,true && expression总是计算为expression,并且false && expression总是计算为false

因此,如果条件为true,则紧随其后的元素&&将出现在输出中。如果是false,React将忽略并跳过它。来源: React Docs中的条件渲染

最后,您可以再次测试您的代码。更改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 ]
);
...
}

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

现在您可以将相应的元素添加到您的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>
</>
);

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

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

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

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

区块设置

区块设置

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

查找文章作者

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

要获取文章作者的ID,您将从数据存储区core/editor导入getCurrentPostAttribute selector

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;
}

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

编辑器中的作者区块

编辑器中的作者区块

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

构建要在页面上呈现的区块

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

正如我们前面提到的,当涉及到动态区块时,插件文件负责生成要在前端呈现的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输出,您会注意到它是一个简单的文章列表,如下图所示:

一个简单的文章列表

一个简单的文章列表

在我们之前的文章中,我们提到您将使用useBlockPropsReact钩子在JSX代码中标记区块的wrapper元素。您需要在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中找到示例区块的完整代码。

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

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

官方资源

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

JavaScript、React和Redux资源

相关资源

小结

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

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

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

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

评论留言