社区新闻

词语切换器:通过交互性扩展核心区块

查看官方原文 ↗ 发布于

我最近看到一个网站,其主页上有一个很酷的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/paragraphcore/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)));
        }
    }
}