WordPress Interactivity API 解锁全新可能

WordPress Interactivity API 解锁全新可能

WordPress 块开发我们之前已经聊过不少:从静态块到动态块,再到核心块的扩展,这些方法虽能创建功能块,但本质上仍是静态的——无法实时响应用户交互。

本文将介绍一种全新的块开发方式,借助 WordPress Interactivity API 创建交互式块。该 API 于 WordPress 6.5 引入,可让块实时响应用户交互,从而打造更吸引人、更动态的用户体验。

内容丰富,下面来看必要的前提条件。

使用 Interactivity API 前需要准备什么

Interactivity API 基于 React,因此需要具备基本的服务端 JavaScript 和 React 知识,以及 npm 和 npx 等构建工具的使用经验。此外,还需深入理解 WordPress 开发Gutenberg 块编辑器

掌握必要技能后,需要一个本地开发环境来快速启动 WordPress 网站。建议使用 DevKinsta,这是专为 WordPress 设计的本地开发套件。只需几次点击即可搭建全新的本地 WordPress 站点,并可进行详细定制。

在 DevKinsta 中创建新 WordPress 项目时,可设置以下选项:

  • 顶级域名:默认为 .local
  • PHP 版本
  • 数据库名称
  • 启用 HTTPS
  • WordPress 详情
  • WordPress 自动更新
  • 多站点

此外,还可从备份中导入现有的 MyKinsta 网站。

在 DevKinsta 中配置本地网站

在 DevKinsta 中配置本地网站

什么是 Interactivity API?

Interactivity API 是 WordPress 原生 API,可为 Gutenberg 块添加交互性,为 WordPress 网站的文章和页面增添动态功能。这是一个轻量级、现代化的解决方案,采用声明式方法处理用户交互。

从零创建交互式块需要高级 PHP 和服务端 JavaScript 开发技能。WordPress 提供了创建交互式块的模板,无需在每个项目中重复造轮子:

npx @wordpress/create-block --template @wordpress/create-block-interactive-template

该模板包含创建交互式块的完整内容,还包括两个示例项目:切换当前主题的按钮,以及展开/折叠段落的按钮。

首先,打开命令行工具,进入本地 WordPress 安装目录下的 Plugins 文件夹,然后执行以下命令:

npx @wordpress/create-block your-interactive-block --template @wordpress/create-block-interactive-template

等待安装完成后,使用偏好的代码编辑器打开项目文件夹。推荐使用 Visual Studio Code,当然也可以使用任何熟悉的编辑器。

Visual Studio Code 中的交互式块

@wordpress/create-block-interactive-template 提供的交互式块项目

进入新插件文件夹,执行以下命令启动开发服务器:

npm start

此后,对块所做的任何更改都将在 WordPress 安装中实时显示。

接下来,在 WordPress 管理后台进入 插件 屏幕,激活刚刚创建的 Interactivity API 插件。创建一篇新文章或页面,然后在块插入器中搜索 Your interactive block,将其添加到内容中。保存文章并在前端预览,你会看到一个黄色块,其中包含两个按钮:第一个用于更改块的背景颜色,第二个用于显示或隐藏段落内容。

交互式块示例

@wordpress/create-block-interactive-template 提供的交互式块示例

现在你已有了一个可供参考的插件,接下来可以更深入地探索交互式块。

交互式块的结构

交互式块的结构与传统块相同,同样需要 package.jsonblock.jsonedit.js 文件和 style.scss 文件。此外,还需要 render.php 文件用于服务端渲染,以及 view.js 文件用于处理前端交互。

下面通过分解启动项目的各个文件,查看交互式块的具体组成部分。

package.json

package.json 文件用于标识 Node 项目、管理脚本,以及在开发过程中管理和安装依赖项。

以下是 create-block-interactive-template 提供的交互式块的 package.json

{
	"name": "your-interactive-block",
	"version": "0.1.0",
	"description": "An interactive block with the Interactivity API.",
	"author": "The WordPress Contributors",
	"license": "GPL-2.0-or-later",
	"main": "build/index.js",
	"scripts": {
		"build": "wp-scripts build --experimental-modules",
		"format": "wp-scripts format",
		"lint:css": "wp-scripts lint-style",
		"lint:js": "wp-scripts lint-js",
		"packages-update": "wp-scripts packages-update",
		"plugin-zip": "wp-scripts plugin-zip",
		"start": "wp-scripts start --experimental-modules"
	},
	"dependencies": {
		"@wordpress/interactivity": "latest"
	},
	"files": [
		"[^.]*"
	],
	"devDependencies": {
		"@wordpress/scripts": "^30.24.0"
	}
}

其中 scriptsdependencies 部分尤为重要:

  • build:将源代码编译为生产环境用的 JavaScript。--experimental-modules 选项支持 WordPress 脚本模块。
  • start:启动开发服务器。同样需要指定 --experimental-modules 选项。
  • dependencies:包含运行时依赖项,以及最新版的 Interactivity API 包。

block.json

block.json 是 Gutenberg 块的清单文件,用于指定要加载的元数据、媒体、脚本和样式。默认情况下,create-block-interactive-template 生成的 block.json 如下:

{
	"$schema": "https://schemas.wp.org/trunk/block.json",
	"apiVersion": 3,
	"name": "create-block/your-interactive-block",
	"version": "0.1.0",
	"title": "Your Interactive Block",
	"category": "widgets",
	"icon": "media-interactive",
	"description": "An interactive block with the Interactivity API.",
	"example": {},
	"supports": {
		"interactivity": true
	},
	"textdomain": "your-interactive-block",
	"editorScript": "file:./index.js",
	"editorStyle": "file:./index.css",
	"style": "file:./style-index.css",
	"render": "file:./render.php",
	"viewScriptModule": "file:./view.js"
}

以下字段对交互式块至关重要:

  • apiVersion:值为 3,是最新版块 API,支持脚本模块等最新特性。
  • supports:指定块的支持项。"interactivity": true 用于添加 Interactivity API 支持。
  • render:指定负责前端渲染的 PHP 文件,用于添加块交互性相关的指令。
  • viewScriptModule:包含交互逻辑的 JavaScript 文件,仅在前端加载,且仅当页面包含交互式块时才会加载。

render.php

render.php 用于构建动态块标记。要使块具有交互性,需添加属性使块的 DOM 元素具备交互能力。

启动项目中的 render.php 文件如下:

<?php
/**
 * PHP file to use when rendering the block type on the server to show on the front end.
 *
 * The following variables are exposed to the file:
 *     $attributes (array): The block attributes.
 *     $content (string): The block default content.
 *     $block (WP_Block): The block instance.
 *
 * @see https://github.com/WordPress/gutenberg/blob/trunk/docs/reference-guides/block-api/block-metadata.md#render
 */

// Generates a unique id for aria-controls.
$unique_id = wp_unique_id( 'p-' );

// Adds the global state.
wp_interactivity_state(
	'create-block',
	array(
		'isDark'    => false,
		'darkText'  => esc_html__( 'Switch to Light', 'your-interactive-block' ),
		'lightText' => esc_html__( 'Switch to Dark', 'your-interactive-block' ),
		'themeText'	=> esc_html__( 'Switch to Dark', 'your-interactive-block' ),
	)
);
?>

<div
	<?php echo get_block_wrapper_attributes(); ?>
	<?php echo wp_interactivity_data_wp_context( array( 'isOpen' => false ) ); ?>
>
	<button
	></button>

<button
		aria-controls="<?php echo esc_attr( $unique_id ); ?>"
	>
		<?php esc_html_e( 'Toggle', 'your-interactive-block' ); ?>
	</button>

<p
	>
		<?php
			esc_html_e( 'Your Interactive Block - hello from an interactive block!', 'your-interactive-block' );
		?>
	</p>
</div>

以上代码的作用如下:

  • wp_interactivity_state:获取或设置 Interactivity API 存储的初始全局状态。
  • data-wp-interactive:在 DOM 元素及其子元素上启用 Interactivity API。其值必须是插件或块的唯一命名空间。
  • wp_interactivity_data_wp_context():生成 data-wp-context 指令,为特定 HTML 节点及其子元素提供本地状态。
  • data-wp-watch:在节点创建时以及状态或上下文每次变化时运行回调。
  • data-wp-class--dark-theme:添加或移除 HTML 元素的 dark-theme 类。
  • data-wp-on--click:同步运行点击事件处理代码。
  • data-wp-text:设置 HTML 元素的内部文本。
  • data-wp-bind--aria-expandeddata-wp-bind--hidden:根据布尔值或字符串值在相应元素上设置 HTML 属性(aria-expandedhidden)。

view.js

此文件用于定义存储,包含块行为所需的逻辑和数据,包括状态、动作和回调。

以下是启动项目生成的 view.js 文件:

/**
 * WordPress dependencies
 */
import { store, getContext } from '@wordpress/interactivity';

const { state } = store( 'create-block', {
	state: {
		get themeText() {
			return state.isDark ? state.darkText : state.lightText;
		},
	},
	actions: {
		toggleOpen() {
			const context = getContext();
			context.isOpen = ! context.isOpen;
		},
		toggleTheme() {
			state.isDark = ! state.isDark;
		},
	},
	callbacks: {
		logIsOpen: () => {
			const { isOpen } = getContext();
			// Log the value of `isOpen` each time it changes.
			console.log( `Is open: ${ isOpen }` );
		},
	},
} );
  • store:用于创建和注册块全局状态和逻辑的主要函数。
  • getContext:在动作和回调中使用的函数,用于访问触发事件的 DOM 元素的本地状态(context)。
  • state:定义块的全局响应式数据。
  • actions:包含定义逻辑和改变状态的函数。
  • callbacks:包含响应特定事件或状态变化而自动执行的函数。

内容较多,阅读完以下章节后,一切都会更加清晰。

下面来看 Interactivity API 的核心概念:指令、存储、状态、动作和回调。

Interactivity API 指令

与 Alpine.js 和 Vue.js 等其他前端库类似,Interactivity API 使用特殊的 HTML 属性,使你能够响应页面事件、更新应用状态、操作 DOM、应用样式、处理用户输入等。

这些属性被称为指令,用于将标记与底层 JavaScript 逻辑连接起来。

以下是最常用的指令列表:

功能指令描述
激活/命名空间data-wp-interactive为元素及其子元素激活 API。值必须设置为插件的唯一标识符。
本地状态data-wp-context为当前元素及其所有子元素提供本地状态("上下文")。它接受一个 JSON 对象。建议使用 wp_interactivity_data_wp_context() 在 PHP 中设置(通常在 render.php 中)。
属性绑定data-wp-bind--[attribute]根据响应式状态或上下文值(布尔值或字符串值)设置 HTML 属性(如 disabledvalue)。
文本修改data-wp-text设置元素的内部文本内容。仅接受字符串。
CSS 类切换data-wp-class--[classname]根据布尔值添加或移除 CSS 类。
行内样式data-wp-style--[css-property]根据布尔值添加或移除行内样式。
事件处理data-wp-on--[event]响应标准 DOM 事件(如 clickmouseover)执行代码。
初始执行data-wp-init仅在节点创建时运行一次回调函数。
状态监听data-wp-watch在节点创建时以及状态或上下文变化时运行回调。
列表迭代data-wp-each渲染元素列表。

如需完整的指令列表,请查阅 Interactivity API 开发笔记API 参考文档

全局状态、本地上下文和派生状态

使用 Interactivity API 之前,必须熟悉前端开发中状态管理的基本概念。经常使用 React、Vue 或 Angular 开发的开发者对这些概念应该已经熟悉。以下是一些通用定义,有助于刚接触这些技术的开发者理解。

全局状态

全局状态指可从应用几乎所有组件访问的数据集。以 Interactivity API 为例,全局状态会影响页面上的所有交互式块,使它们保持同步。例如,当用户将产品添加到购物车时,这一操作会反映在购物车块上。

使用 Interactivity API 时,应使用 wp_interactivity_state() 函数在服务端设置全局状态的初始值。在上述启动项目中,该函数在 render.php 文件中的使用方式如下:

// Adds the global state.
wp_interactivity_state(
	'create-block',
	array(
		'isDark'    => false,
		'darkText'  => esc_html__( 'Switch to Light', 'your-interactive-block' ),
		'lightText' => esc_html__( 'Switch to Dark', 'your-interactive-block' ),
		'themeText'	=> esc_html__( 'Switch to Dark', 'your-interactive-block' ),
	)
);

该函数接受两个参数:

  • 存储命名空间的唯一标识符。本例中为 create-block
  • 将与现有存储命名空间合并的数据数组(如果存在)。

初始全局状态值随后用于渲染页面。可直接在指令属性值中使用 state 来访问全局状态值:

<button
></button>

store() 函数提供了从 JavaScript 访问全局状态的主要入口,仅限于选定的命名空间。回到启动项目代码,store() 函数在 view.js 文件中的使用方式如下:

import { store, getContext } from '@wordpress/interactivity';

const { state } = store( 'create-block', {
	state: { ... },
	actions: { ... },
	callbacks: { ... },
} );

访问全局状态时,使用 state 属性:

actions: {
	toggleTheme() {
		state.isDark = ! state.isDark;
	},
},

本地上下文

本地上下文是只能由特定组件及其直接子元素访问的数据。WordPress 交互式块为块及其嵌套元素提供独立的状态。

使用 Interactivity API 时,可使用 getContext() 函数访问本地上下文。再看启动项目,当用户点击 Toggle 按钮时,会触发 toggleOpen() 动作,从而访问组件的本地上下文:

actions: {
	toggleOpen() {
		const context = getContext();
		context.isOpen = ! context.isOpen;
	},
},
  • getContext():获取块的本地状态对象。此对象的属性在组件标记(render.php)中使用 wp_interactivity_data_wp_context() 函数定义。
  • context.isOpen = ! context.isOpen;:切换组件本地上下文中 isOpen 属性的值。

派生状态

派生状态指根据现有全局或本地状态动态计算的数据。

例如,view.js 文件中的以下代码:

const { state } = store( 'create-block', {
	state: {
		get themeText() {
			return state.isDark ? state.darkText : state.lightText;
		},
	},
	...
}

该代码在 create-block 命名空间的全局状态内定义了 themeText 派生状态。

get themeText() 不是固定值,而是一个 getter 函数,每次读取 themeText 属性时都会执行。它不应像普通函数那样调用,因为 Interactivity API 会将其视为状态属性,并在其他状态属性值发生变化时自动重新计算。在上述代码中,每当 isDark 属性值发生变化时,themeText 属性值都会重新计算:如果 state.isDarktrue,则取 state.darkText 的值;否则取 state.lightText 的值。

如需更全面地了解本节所述的概念,请参阅理解全局状态、本地上下文和派生状态

动作和回调

动作和回调决定了用户交互和状态变化的响应方式。

交互式块的 actions 部分包含响应用户生成的事件而执行的函数。这些函数主要用于修改组件的本地或全局状态。以下是 view.js 文件中的相关代码:

actions: {
	toggleOpen() {
		const context = getContext();
		context.isOpen = ! context.isOpen;
	},
	...
},
  • 在这段代码中,toggleOpen() 函数使用 getContext() 访问触发该动作的块的本地上下文,以切换 isOpen 属性的值。

同理,也可以访问全局状态:

actions: {
	...,
	toggleTheme() {
		state.isDark = ! state.isDark;
	},
},
  • toggleTheme() 函数直接访问全局 state 对象,并改变 isDark 属性的值。

动作通过 data-wp-on--[event] 指令触发。例如,render.php 文件中的按钮定义如下:

<button
	aria-controls="<?php echo esc_attr( $unique_id ); ?>"
>
  • 在此 HTML 代码中,data-wp-on--click 属性会在用户点击切换按钮时触发 toggleOpen 动作。

callbacks 部分包含在依赖数据发生变化时自动执行的函数。这些函数的目的是响应状态变化而产生副作用。

create-block-interactive-template 生成的基础项目中,可以找到以下回调:

callbacks: {
	logIsOpen: () => {
		const { isOpen } = getContext();
		// Log the value of `isOpen` each time it changes.
		console.log( `Is open: ${ isOpen }` );
	},
},
  • logIsOpen 函数使用了本地上下文中的 isOpen 变量。
  • 回调使用 getContext() 获取 isOpen 的值。
  • 每次 isOpen 值发生变化时,该函数都会向浏览器控制台输出一条消息。

控制台中的消息通知用户本地上下文的变化。

控制台中的消息通知用户本地上下文的变化。

构建交互式块

理论部分已经介绍完毕,现在是时候动手写代码了!在本指南的第二部分,你将学习如何创建一个交互式块,允许用户将产品添加到理想购物车中,并自动更新数量和总计。这是一个演示示例,但希望它能清楚地说明如何使用状态、动作和回调。

编辑器中的交互式块

编辑器中的交互式块

我们将使用 create-block-interactive-template 创建一个名为 Interactive Counter 的块。首先,打开命令行工具并输入以下命令:

npx @wordpress/create-block interactive-counter --template @wordpress/create-block-interactive-template

然后,进入新项目目录并运行首次构建。

cd interactive-counter && npm run build

现在在代码编辑器中打开项目。在 /src 目录中查找 block.json 文件,内容应类似于以下内容:

{
	"$schema": "https://schemas.wp.org/trunk/block.json",
	"apiVersion": 3,
	"name": "create-block/interactive-counter",
	"version": "0.1.0",
	"title": "Interactive Counter",
	"category": "widgets",
	"icon": "media-interactive",
	"description": "An interactive block with the Interactivity API.",
	"supports": {
		"interactivity": true
	},
	"textdomain": "interactive-counter",
	"editorScript": "file:./index.js",
	"editorStyle": "file:./index.css",
	"style": "file:./style-index.css",
	"render": "file:./render.php",
	"viewScriptModule": "file:./view.js"
}

edit.js 文件

下一步是创建编辑器中显示的块。修改 /src/edit.js 文件如下:

import { __ } from '@WordPress/i18n';
import { useBlockProps } from '@WordPress/block-editor';
import './editor.scss';

export default function Edit({ attributes, setAttributes }) {
	const blockProps = useBlockProps();
	const products = [
		{ id: 'product1', name: __('Product 1', 'interactive-counter'), price: 10.00 },
		{ id: 'product2', name: __('Product 2', 'interactive-counter'), price: 15.00 },
		{ id: 'product3', name: __('Product 3', 'interactive-counter'), price: 20.00 },
	];

return (
		<div {...blockProps}>
			<h3>{__('Shopping Cart', 'interactive-counter')}</h3>
			<ul>
				{products.map((product) => (
					<li key={product.id} style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '10px' }}>
						<span style={{ flex: 1 }}>{product.name} - ${product.price.toFixed(2)}</span>
						<div style={{ display: 'flex', gap: '10px', alignItems: 'center' }}>
							<button disabled>-</button>
							<span>0</span>
							<button disabled>+</button>
						</div>
						<span style={{ flex: 1, textAlign: 'right' }}>
							{__('Subtotal:', 'interactive-counter')} $0.00
						</span>
					</li>
				))}
			</ul>
			<div style={{ borderTop: '1px solid #ccc', paddingTop: '15px' }}>
				<p style={{ display: 'flex', justifyContent: 'space-between' }}>
					<strong>{__('Subtotal:', 'interactive-counter')}</strong>
					<span>$0.00</span>
				</p>
				<p style={{ display: 'flex', justifyContent: 'space-between' }}>
					<strong>{__('Tax (22%):', 'interactive-counter')}</strong>
					<span>$0.00</span>
				</p>
				<p style={{ display: 'flex', justifyContent: 'space-between' }}>
					<strong>{__('Total:', 'interactive-counter')}</strong>
					<span>$0.00</span>
				</p>
			</div>
			<p>{__('Quantities and totals will be interactive in the frontend.', 'interactive-counter')}</p>
		</div>
	);
}

该代码在编辑器中生成一个自定义块,仅前端具有交互性。关于 /src/edit.js 文件的更多详细信息,请参阅 Gutenberg 块开发指南。

render.php 文件

接下来编辑 /src/render.php 文件。将现有代码替换为以下内容:

<?php
/**
 * Render callback for the interactive-counter block.
 */

$products = [
	['id' => 'product1', 'name' => __('Product 1', 'interactive-counter'), 'price' => 10.00],
	['id' => 'product2', 'name' => __('Product 2', 'interactive-counter'), 'price' => 15.00],
	['id' => 'product3', 'name' => __('Product 3', 'interactive-counter'), 'price' => 20.00],
];

// Initialize global state
wp_interactivity_state('interactive-counter', [
	'products' => array_map(function ($product) {
		return [
			'id' => $product['id'],
			'name' => $product['name'],
			'price' => $product['price'],
			'quantity' => 0,
			'subtotal' => '0.00',
		];
	}, $products),
	'vatRate' => 0.22,
]);

代码功能如下:

  • 首先创建产品硬编码数组,每个产品包含 ID、名称和价格。
  • 使用 wp_interactivity_state 初始化全局状态,第一个参数是存储名称,必须与 view.js 中使用的名称一致。
  • 将产品数组映射为新的 products 数组,向原始数组属性中添加数量和小计,这个新数组提供了在 view.js 中使用的数据结构。
  • vatRate 用于设置税率默认值。

接下来,将以下内容添加到上述代码中:

<div <?php echo get_block_wrapper_attributes(); ?>>
	<h3><?php echo esc_html__('Cart', 'interactive-counter'); ?></h3>
	<ul>
		<?php foreach ($products as $index => $product) : ?>
			<li data-wp-context='{
				"productId": "<?php echo esc_attr($product['id']); ?>",
				"quantity": 0,
				"subtotal": "0.00"
			}'
			data-wp-bind--data-wp-context.quantity="state.products[<?php echo $index; ?>].quantity"
			data-wp-bind--data-wp-context.subtotal="state.products[<?php echo $index; ?>].subtotal">
				<span><?php echo esc_html($product['name']); ?> - $<?php echo esc_html(number_format($product['price'], 2)); ?></span>
				<div>
					<button>-</button>
					<span>0</span>
					<button>+</button>
				</div>
				<span>
					<?php echo esc_html__('Subtotal:', 'interactive-counter'); ?>
					$<span>0.00</span>
				</span>
			</li>
		<?php endforeach; ?>
	</ul>
	<div>
		<p>
			<strong><?php echo esc_html__('Subtotal:', 'interactive-counter'); ?></strong>
			$ <span>0.00</span>
		</p>
		<p>
			<strong><?php echo esc_html__('Tax (22%):', 'interactive-counter'); ?></strong>
			$ <span>0.00</span>
		</p>
		<p>
			<strong><?php echo esc_html__('Total:', 'interactive-counter'); ?></strong>
			$ <span>0.00</span>
		</p>
	</div>
</div>

代码功能如下:

  • div 容器中的 get_block_wrapper_attributes() 是 WordPress 函数,用于生成块的標準属性。本例中生成了 class 属性 "wp-block-create-block-interactive-counter"
  • data-wp-interactive 属性使此块具有交互性。
  • data-wp-init 属性触发 view.js 中定义的 init 回调。
  • foreach 循环为 products 数组中的每个产品生成一个列表项。
  • data-wp-context 定义块的本地上下文。
  • data-wp-binddata-wp-context.quantity 的值绑定到全局 state.products[$index].quantity 属性。
  • 下一行同样绑定小计。
  • 以下两个按钮通过 data-wp-on--click 属性触发 decrementincrement 动作。
  • span 中的 data-wp-text 属性根据 context.quantity 的当前值更新元素内容。

其余代码不言自明,下面来看下一个文件。

view.js 文件

此文件包含交互式块的逻辑。

import { store, getContext } from '@WordPress/interactivity';

store('interactive-counter', { state: { get subtotal() { const { products } = store('interactive-counter').state; return products .reduce((sum, product) => sum + product.price * (product.quantity || 0), 0) .toFixed(2); }, get vat() { const { subtotal, vatRate } = store('interactive-counter').state; return (subtotal * vatRate).toFixed(2); }, get total() { const { subtotal, vat } = store('interactive-counter').state; return (parseFloat(subtotal) + parseFloat(vat)).toFixed(2); }, }, actions: { increment: () => { const context = getContext(); const { products } = store('interactive-counter').state; const product = products.find(p => p.id === context.productId); if (product) { product.quantity = (product.quantity || 0) + 1; product.subtotal = (product.price * product.quantity).toFixed(2); context.quantity = product.quantity; context.subtotal = product.subtotal; console.log(Incremented ${context.productId}:, { quantity: product.quantity, subtotal: product.subtotal, context }); } else { console.warn('Product not found:', context.productId); } }, decrement: () => { const context = getContext(); const { products } = store('interactive-counter').state; const product = products.find(p => p.id === context.productId); if (product && (product.quantity || 0) > 0) { product.quantity -= 1; product.subtotal = (product.price * product.quantity).toFixed(2); context.quantity = product.quantity; context.subtotal = product.subtotal; console.log(Decremented ${context.productId}:, { quantity: product.quantity, subtotal: product.subtotal, context }); } else { console.warn('Cannot decrement:', context.productId, product?.quantity); } }, }, callbacks: { init: () => { const { products } = store('interactive-counter').state; products.forEach((product, index) => { product.quantity = 0; product.subtotal = '0.00'; console.log(Initialized product ${index}:, { id: product.id, quantity: product.quantity, subtotal: product.subtotal }); }); }, }, });


该文件为 `interactive-counter` 命名空间定义了存储,管理状态、动作和回调:

store('interactive-counter', { state: { ... }, actions: { ... }, callbacks: { ... }, });


具体来看各部分的作用:

- `state`:定义了三个计算属性(getter):`subtotal`、`vat` 和 `total`。这些函数从全局状态中获取值并计算返回结果。
- `actions`:定义了两个事件处理函数:`increment` 和 `decrement`。这两个函数从全局状态获取 `products` 数组,根据 `context.productId` 从本地上下文获取当前产品,更新产品属性值(`quantity` 和 `subtotal`),并将本地上下文与新值同步。
- `callbacks`:定义了初始化用的 `init` 回调。

下图展示了前端中的交互式块。

![使用 Interactivity API 构建的交互式计数器](/uploads/76797ea2-fd8a-4357-917b-ac36131aae84.webp)

*使用 Interactivity API 构建的交互式计数器*

## 总结

本文介绍了 WordPress Interactivity API 的主要特性,深入探讨了全局状态、本地上下文、指令、动作和回调等关键概念。还学习了如何使用 `@wordpress/create-block-interactive-template` 从头创建交互式块,并通过创建一个与用户输入交互的真实块进行了实践。

希望本指南能提供必要的工具和知识,帮助你使用 WordPress Interactivity API 创建出色的动态交互式网站。

祝编码愉快!
ESC 关闭