词语切换器:通过交互性扩展核心区块
我最近看到一个网站,其主页上有一个很酷的CSS效果,词语在不同变体之间平滑过渡。
自然地,我必须一探究竟。该网站是用WordPress构建的,效果是使用页面构建器完成的。这让我想到:“嘿,这可以做成一个很棒的WordPress区块!而且我可以不依赖任何外部库来构建它。”
我开始制作一个自定义区块的原型,但很快意识到扩展现有的核心区块更有意义。事实证明,这是一个结合多个WordPress API在现有功能之上添加自定义行为的绝佳示例:
我给自己设定了一个挑战:创建一个可用于生产环境的版本,以展示这些组合API的强大功能。
编辑器体验也很重要
WordPress的特别之处在于:它不仅帮助你构建出色的用户体验,还为你提供了构建出色编辑体验的工具。那个词语切换效果?当然,前端只需要20行JavaScript。但WordPress挑战你思考得更全面:内容创作者将如何实际使用这个功能?
这正是WordPress作为开发框架的闪光点。当其他平台只关注访客体验时,WordPress为双方提供了专门的API:
对于内容编辑者,WordPress提供:
- Format API,用于通过直观控件扩展编辑工具栏。
- 视觉反馈,准确显示将要动画化的内容。
- 熟悉的界面来格式化内容。
对于网站访客,WordPress提供:
- Interactivity API,用于流畅、高性能的动画,无需外部库
- HTML API,用于服务器端处理并确保干净、语义化的HTML
- 高效加载的现代JavaScript模块
这个词语切换器项目完美地展示了这种双重关注理念。我们做出的每一个技术决策都服务于两个受众:
- 编辑者获得一种无缝的方式来标记可切换文本,无需代码
- 访客看到快速、流畅的动画,而不会拖慢网站速度。
- 开发者获得干净、可维护的代码,充分利用WordPress核心
这就是WordPress的方式:不仅要解决前端问题,还要解决整个内容生命周期的问题。而且你不必从头开始;你可以在现有基础上构建。让我们看看这些API如何协同工作来实现这一点。
我们要构建什么
在本文中,我们将创建一个功能,允许编辑者标记某些文本区域,其中的词语将动态切换。例如,文本“I’m a developer, designer, creator”可以被标记,使得以逗号分隔的词语在前端通过动画循环切换——全部使用原生的WordPress工具构建,无外部依赖。
先决条件
- WordPress开发环境(WordPress Studio, wp-env等)
- 已安装Node.js和npm
- React和WordPress区块开发的基础知识
- 熟悉PHP
该项目的最终代码位于 https://github.com/wptrainingteam/word-switcher-core-blocks,你也可以通过查看仓库中的提交记录来跟随本文解释的不同步骤。
概述:四项核心技术
对于我们的项目,我们将使用Interactivity API的数据属性(指令)使HTML的特定部分响应前端JavaScript驱动的状态变化。在将区块的标记返回给浏览器之前,我们会在段落和标题块中查找编辑者使用我们的自定义格式(通过Format API注册)特别标记的文本。每当找到此类标记的文本时,我们就使用HTML API将适当的Interactivity API指令注入到这些部分。结果,只有预期的词语或短语在前端平滑动画——干净地将编辑者的选择与访客的交互效果连接起来。
在深入代码之前,让我们了解一下使这个项目成为可能的关键WordPress API:
Interactivity API
Interactivity API是WordPress用于向前端添加动态行为的声明式系统。你无需编写直接操作DOM的命令式JavaScript,而是通过HTML属性(data-wp-*)声明行为。此API管理状态、处理响应性,并在标记和行为之间提供清晰的分离。
JavaScript Modules
WordPress现在支持原生ES模块,允许更好的代码组织和性能。模块异步加载,可以延迟,并提供更好的tree-shaking能力。我们将使用模块在浏览器中高效加载我们的交互式前端代码。
Format API
Format API允许开发者使用自定义内联格式化选项扩展富文本编辑器。就像粗体或斜体格式化一样,你可以创建出现在工具栏中的自定义格式。在我们的项目中,我们将使用它让编辑者将特定文本标记为“可切换”——即在前端将在不同词语之间动画化的文本。
HTML API (WP_HTML_Tag_Processor)
WordPress的HTML API提供了一种安全、可靠的方式来在服务器端解析和修改HTML内容。与正则表达式或字符串操作不同,它理解HTML结构并正确处理边缘情况。我们将使用它来处理标记的内容,并为其前端交互性做准备。
设置插件结构
让我们从创建插件结构开始。在这个项目中,我们将使用动态功能扩展WordPress的核心区块(段落、标题),而不是创建全新的区块。
首先,在你的WordPress安装中创建一个新的插件目录:
cd wp-content/plugins
mkdir word-switcher-core-blocks
cd word-switcher-core-blocks
创建主插件文件 plugin.php:
<?php
/**
* Plugin Name: Word Switcher Core Blocks
* Description: Example block scaffolded with Create Block tool.
* Version: 0.1.0
* Requires at least: 6.7
* Requires PHP: 7.4
* Author: The WordPress Contributors
* License: GPL-2.0-or-later
* Text Domain: word-switcher
*/
if (!defined('ABSPATH')) {
exit; // Exit if accessed directly.
}
// We'll add our functions here as we build
创建此文件后,你现在可以转到WordPress管理后台,导航到插件屏幕,并激活你的“Word Switcher Core Blocks”插件。
接下来,创建一个包含这些构建脚本的package.json文件,并运行npm install来安装依赖项:
{
"name": "word-switcher",
"version": "0.1.0",
"scripts": {
"build": "wp-scripts build --experimental-modules",
"start": "wp-scripts start --experimental-modules"
},
"devDependencies": {
"@wordpress/scripts": "^30.15.0"
}
}
--experimental-modules标志在构建过程中启用了对现代JavaScript模块的支持,这是Interactivity API正常运行所必需的。
接下来,在你的插件文件夹内创建以下目录结构,以及我们将在本文中逐步填充的初始文件:
├── resources
│ ├── css
│ │ └── word-switcher-styles.scss # word-switcher 样式和动画
│ └── js
│ ├── register-format-type.js # 处理自定义文本格式化 (Format API)
│ └── word-switcher-store.js # 管理状态和过渡 (Interactivity API)
├── plugin.php # 主插件逻辑
└── package.json # 构建工具和依赖项
└── webpack.config.js # 自定义webpack配置
在实现核心功能时,我们将逐步在这些文件中工作。
实现Format API
让我们首先使用Format API添加一个工具栏按钮。这将允许编辑者高亮显示文本(以逗号分隔的词语)并将其标记为词语切换。我们将用一个特殊的标签包装此文本,以便稍后可以在PHP中检测和处理它。
理解Format API
Format API通过向工具栏添加自定义内联格式化选项来扩展WordPress的富文本编辑器。当你选择文本并应用粗体格式化时,你就在使用Format API。我们将为标记“可切换”文本创建类似的体验。
关键概念:
- Format Types:可应用于选定文本的自定义格式
- Toolbar Buttons:选择文本时出现的UI控件
- HTML Markup:应用于内容的实际标签和类
创建Format Type
创建文件resources/js/register-format-type.js:
import { toggleFormat, registerFormatType } from "@wordpress/rich-text";
import { RichTextToolbarButton } from "@wordpress/block-editor";
const WORD_SWITCHER_FORMAT_TYPE = "word-switcher/format-type-delimiter";
registerFormatType(WORD_SWITCHER_FORMAT_TYPE, {
title: "Word Switcher",
tagName: "span",
className: "word-switcher",
edit: ({ isActive, onChange, value }) => {
return (
<RichTextToolbarButton
icon="update"
title="Mark as Word Switcher Area"
onClick={() => {
onChange(
toggleFormat(value, {
type: WORD_SWITCHER_FORMAT_TYPE,
}),
);
}}
isActive={isActive}
/>
);
},
});
这会创建一个工具栏按钮,将选定的文本包装在<span class="word-switcher">标签中。当编辑者选择像“developer, designer, creator”这样的文本并单击此按钮时,Format API会将其包装在我们的自定义span标签中,为服务器端处理做准备。
我们需要创建一个自定义webpack配置文件来配置我们的自定义入口点。创建以下webpack.config.js:
const [
defaultConfigNonModule,
defaultConfigModule,
] = require("@wordpress/scripts/config/webpack.config");
const path = require("path");
module.exports = [
// Non Module config
{
...defaultConfigNonModule,
entry: {
"js/register-format-type": path.resolve(
process.cwd(),
"resources/js",
"register-format-type.js"
)
},
},
// Module config
{
...defaultConfigModule
},
];
为自定义入口点创建自定义webpack配置文件的过程,尤其是在使用JavaScript模块时,已在WordPress开发者博客的其他文章中介绍过。请查看使用Interactivity API构建明暗切换 > 配置webpack。
现在我们已经设置了webpack.config.js,让我们运行npm start来获取build/js/register-format-type.js(编译后的脚本)和build/js/register-format-type.asset.php(包含依赖项和版本详细信息)。
在plugin.php中注册并排队此脚本:
function word_switcher_core_blocks_register_assets() {
$dir = plugin_dir_path( __FILE__ );
define( 'WS_FORMAT_SCRIPT', 'word-switcher-core-blocks-register-format-type' );
// Register the Format API script for the editor.
$script_asset = require "$dir/build/js/register-format-type.asset.php";
wp_register_script(
WS_FORMAT_SCRIPT,
plugins_url( 'build/js/register-format-type.js', __FILE__ ),
$script_asset['dependencies'],
$script_asset['version'],
true
);
}
add_action('init', 'word_switcher_core_blocks_register_assets');
// Enqueue format type in the editor
function word_switcher_core_blocks_enqueue_block_editor_assets() {
wp_enqueue_script( WS_FORMAT_SCRIPT );
}
add_action('enqueue_block_editor_assets', 'word_switcher_core_blocks_enqueue_block_editor_assets');
我们使用enqueue_block_editor_assets action hook来专门为管理屏幕中的区块编辑器排队资源。
现在我们应该有一个新的“Mark as Word Switcher Area”格式化工具,它将把选定的文本包装到一个带有word-switcher类的自定义span中。
使用HTML API进行服务器端HTML处理
通过Format API用特殊的<span>元素标记我们的内容后,我们现在可以在将HTML发送到浏览器之前在服务器上处理它。这正是WordPress的HTML API的闪光点。
理解HTML API
HTML API(特别是WP_HTML_Tag_Processor)是WordPress用于安全解析和修改HTML的解决方案。使用正则表达式或字符串操作的传统方法容易出错,并且可能因复杂的HTML而中断。HTML API:
- 理解HTML结构:它了解标签、属性和嵌套
- 处理边缘情况:正确处理引号、特殊字符和格式错误的HTML
- 提供安全操作:提供添加/修改属性而不破坏标记的方法
- 支持书签:可以在HTML中标记位置以供以后参考
使用HTML API处理核心区块标记
为了拦截和增强核心区块输出,我们将使用render_block_{$this->name}过滤器。此过滤器允许我们在服务器端渲染期间修改区块的HTML内容。通过为core/paragraph和core/heading区块挂钩此过滤器,我们可以将Interactivity API指令功能直接注入到渲染的标记中。
让我们首先将以下代码添加到你的plugin.php文件中:
add_filter( 'render_block_core/paragraph', 'word_switcher_core_blocks_render_block', 10, 2 );
add_filter( 'render_block_core/heading', 'word_switcher_core_blocks_render_block', 10, 2 );
function word_switcher_core_blocks_render_block($block_content, $block) {
// ... code for processing and modifying block content.
return $block_content;
}
开始word_switcher_core_blocks_render_block函数,检查其内容是否不包含<span class="word-switcher">。在这种情况下,直接返回$block_content,无需进一步处理。
// Check if content contains our word-switcher markup
if (strpos($block_content, 'class="word-switcher"') === false) {
return $block_content;
}
初始化HTML API处理器来解析区块的标记,并开始导航到第一个节点(应该是区块的包装器)。我们将设置一个书签以便稍后返回此处。
$processor = new WP_HTML_Tag_Processor($block_content);
// Find the first tag (the block wrapper)
if (!$processor->next_tag()) {
return $block_content;
}
$processor->set_bookmark("parent");
$words = [];
现在我们遍历HTML以找到正确的元素,提取其内容并添加Interactivity API指令:
- 定位word-switcher spans:使用HTML API的标签过滤来查找由Format API标记的spans
- 注入Interactivity指令:添加
<a href="https://developer.wordpress.org/block-editor/reference-guides/interactivity-api/api-reference/#wp-text">data-wp-text</a>以将内容绑定到当前词语状态,并添加<a href="https://developer.wordpress.org/block-editor/reference-guides/interactivity-api/api-reference/#wp-class">data-wp-class--fade</a>用于动画控制 - 提取词语变体:解析逗号分隔的文本内容(例如,“powerful, flexible,”)
// Find all spans with word-switcher class
while ($processor->next_tag(['tag_name' => 'span', 'class_name' => 'word-switcher'])) {
// Add Interactivity API directives
$processor->set_attribute("data-wp-text", "state.currentWord");
$processor->set_attribute("data-wp-class--fade", "context.isFading");
// Extract the comma-separated words
if ($processor->next_token()) {
$text_content = $processor->get_modifiable_text();
if ($text_content) {
$words = array_filter(array_map('trim', explode(',', $text_content)));
}
}
}