社区新闻

命令面板 API 入门指南

查看官方原文 ↗ 发布于

WordPress 6.3 向数百万用户引入了命令面板,并提供了一个API 供开发者扩展其功能,添加额外的命令。

这类功能在其他应用和程序中很常见。在 WordPress 中,你可以按 Mac 上的 Cmd + K 或 Windows 上的 Ctrl + K,屏幕上会出现一个模态框。然后,只需输入或搜索命令:

WordPress 站点编辑器,屏幕上覆盖着命令面板模态框。

截至 WordPress 6.4,命令面板仅在文章编辑器和站点编辑器屏幕中可用。在未来的版本中,它很可能成为整个管理区域的功能。

版本 6.4 优化了界面,并向其内置命令目录添加了更多选项。一些现有命令允许你:

  • 添加新文章/页面
  • 编辑现有文章/页面
  • 访问模式库
  • 切换各种用户偏好设置

这些只是你期望通过键盘导航 WordPress 界面的一些基础命令。但让命令面板真正强大的是 API,它允许你用最少的 JavaScript 代码添加自定义命令。

你可以注册两种类型的命令:静态命令和动态命令。在本教程中,你将学习如何注册静态命令,但你可以在命令面板 API 的开发说明中了解更多关于动态命令(命令加载器)的信息。

先决条件

虽然你将在本文中学习命令面板 API 的基础知识,但这仍然是一个高级教程。这意味着在继续之前,你应该熟悉使用 @wordpress/scripts 包。本教程期望你了解如何运行该包中的 startbuild 命令来处理所需的 JavaScript 代码。

你很可能通过插件添加自定义命令。但是,也可以通过主题添加。要了解如何在主题中设置 @wordpress/scripts,请查看超越区块样式,第 1 部分:在主题中使用 WordPress 脚本包

查阅 @wordpress/commands 文档将加深你对后续代码的理解,但开始阅读本文并非必需。

注册静态命令

有两种注册静态命令的方法:

  • wp.data.dispatch( wp.commands.store ).registerCommand() 操作
  • wp.commands.useCommand() React Hook

它们本质上工作方式相同,接受相同的参数。唯一的区别是 useCommand() Hook 必须在 React 组件内部使用。在组件外部,请使用 registerCommand()。两者都接受一个对象参数,允许你定义几个属性:

  • name 命令的唯一命名空间和别名。
  • label 用于在命令面板中显示命令的标签。
  • searchLabel 一个可选标签,用于检查用户搜索的内容(显示的是 label 参数)。
  • context 一个可选上下文,用于在特定屏幕的默认列表中显示命令。当前的上下文有:
    • site-editor 主站点编辑器屏幕。
    • site-editor-edit 在站点编辑器中编辑模板时。
  • icon 一个 SVG 图标(React 组件),显示在命令面板中标签旁边。
  • callback 用户选择命令时触发的回调函数。

快速看一下调用 registerCommand() 的示例:

wp.data.dispatch( wp.commands.store ).registerCommand( {
	name:  'your-namespace/your-command-slug',
	label: 'Command label',
	searchLabel: 'Command search label',
	icon:  icon,
	context: 'site-editor',
	callback: ( { close } ) => {
		close();
	}
} );

对于在 WordPress 环境中工作的大多数 JavaScript 开发者来说,这段代码应该相当简单。最复杂的部分是决定在你的 callback 函数中做什么,这是你实际执行的命令

构建示例命令插件

让我们一起完成一个练习,构建一个添加几个自定义命令的插件。

设置插件

首先,在你的 WordPress 安装的 wp-content/plugins 文件夹中创建一个新插件,文件结构如下:

  • build/
    • WordPress 会将编译后的文件输出到此文件夹。
  • src/
    • index.js
  • index.php
  • package.json

确保你至少在 package.json 中定义了 startbuild 脚本:

{
	"scripts": {
		"start": "wp-scripts start",
		"build": "wp-scripts build"
	}
}

接下来,通过 CLI 命令安装 @wordpress/scripts 包:

npm install @wordpress/scripts --saveDev

这个插件还需要 @wordpress/icons 包,所以也添加它:

npm install @wordpress/icons

然后,在 index.php 中添加你的插件文件头,应该类似于这样:

<?php
/**
 * Plugin Name:       Dev Blog: Command Palette API
 * Plugin URI:        https://developer.wordpress.org/news/
 * Description:       Showcases examples of adding static commands via the Command Palette API.
 * Version:           1.0.0
 * Requires at least: 6.4
 * Requires PHP:      7.4
 * Author:            WordPress Developer Blog
 * Author URI:        https://developer.wordpress.org/news/
 * Text Domain:       dev-blog
 */

激活示例插件后,别忘了运行 start CLI 命令:

npm run start

加载编辑器脚本

因为这不是一个自定义区块,WordPress 不会自动加载你的 JavaScript,你需要在 enqueue_block_editor_scripts Hook 上加载你的 build/index.js 文件。

将此代码添加到你的插件 index.php 文件中:

add_action( 'enqueue_block_editor_assets', 'devblog_command_api_editor_assets' );
function devblog_command_api_editor_assets() {
	$asset_file = trailingslashit( __DIR__ ) . 'build/index.asset.php';
	if ( file_exists( $asset_file ) ) {
		$asset = include $asset_file;
		wp_enqueue_script(
			'devblog-command-api',
			trailingslashit( plugin_dir_url( __FILE__ ) ) . 'build/index.js',
			$asset['dependencies'],
			$asset['version'],
			true
		);
	}
}

导入依赖项

现在打开你的插件 src/index.js 文件。你将用它来导入添加命令所需的依赖项:

要导入依赖项,请将此添加到你的 src/index.js 文件顶部:

import { store as commandsStore } from '@wordpress/commands';
import { dispatch } from '@wordpress/data';
import { __ } from '@wordpress/i18n';
import { settings, comment, button } from '@wordpress/icons';

现在你可以开始使用命令面板 API 了。尝试以下一些示例。

示例 #1:导航到管理页面命令

对于第一个示例,让我们添加一个简单的命令来访问 WordPress 管理后台的其他页面,如下所示:

WordPress 站点编辑器主屏幕。屏幕上覆盖着命令面板,输入了"gutenberg"搜索词,并显示了下拉命令选项列表。

我选择了一个用于导航到Gutenberg 实验设置屏幕的命令(需要 Gutenberg 插件处于活动状态)。但你可以将下面代码中的 URL 更改为指向你想要的任何网页。

要添加一个导航到另一个页面的命令,你可以使用 document.location JavaScript 对象并设置其 href 属性。尝试将此代码添加到你的 src/index.js 文件中:

dispatch( commandsStore ).registerCommand( {
	name:  'dev-blog/gutenberg-experiments',
	label: __( 'Gutenberg Experiments', 'dev-blog' ),
	icon:  settings,
	context: 'site-editor',
	callback: ( { close } ) => {
		document.location.href = 'admin.php?page=gutenberg-experiments';
		close();
	}
} );

要测试,请打开命令面板并开始输入“Gutenberg Experiments”标签。你应该会看到它作为一个选项出现,选择后,你应该被带到正确的管理屏幕。

示例 #2:切换面板命令

在这个示例中,你将添加一个切换命令,用于启用或禁用文章编辑器中的讨论面板:

WordPress 文章编辑器,屏幕上覆盖着命令面板模态框。显示了用于切换讨论面板的搜索。

只要你知道面板的名称,就可以对任何面板执行此操作。对于讨论面板,它是 discussion-panel

要切换文章编辑器中的面板,你需要将面板名称传递给来自 core/edit-posttoggleEditorPanelEnabled() 操作。

将此代码添加到你的 src/index.js 文件中:

dispatch( commandsStore ).registerCommand( {
	name:  'dev-blog/discussion-panel',
	label: __( 'Toggle discussion panel', 'dev-blog' ),
	icon:  comment,
	callback: ( { close } ) => {
		dispatch( 'core/edit-post' ).toggleEditorPanelEnabled(
			'discussion-panel'
		);
		close();
	}
} );

某些面板可能并非对所有编辑器都可用。例如,讨论面板在文章编辑器中显示,但不在站点编辑器中显示。因此,如果你从站点编辑器使用“切换讨论面板”命令,你将不会实际看到命令的结果。

我不确定这是否是最佳的用户体验。另一种方法是仅在编辑文章屏幕上注册该命令。你可以通过测试 wp.editPost 对象是否已定义来检查你是否在该屏幕上。

尝试将之前的代码包装在条件语句中,以便该命令仅针对文章编辑器加载:

if ( undefined !== wp.editPost ) {
	dispatch( commandsStore ).registerCommand( {
		name:  'dev-blog/discussion-panel',
		label: __( 'Toggle discussion panel', 'dev-blog' ),
		icon:  comment,
		callback: ( { close } ) => {
			dispatch( 'core/edit-post' ).toggleEditorPanelEnabled(
				'discussion-panel'
			);
			close();
		}
	} );
}

示例 #3:切换用户偏好命令

对于最后一个示例,让我们构建一个命令来切换用户偏好,在编辑器 UI 按钮的图标或标签之间切换:

WordPress 站点编辑器,正在编辑单个文章模板。屏幕上覆盖着命令面板模态框,显示了用于切换按钮标签的搜索。

这是另一个类似于前一个示例的上下文相关命令。不同之处在于,偏好设置是根据当前编辑器存储的。一个用于站点编辑器,一个用于文章编辑器。

你已经知道如何检查 wp.editPost 来确定你是否在文章编辑器中。有一个等效的对象用于检查站点编辑器:wp.editSite

要切换用户偏好,请使用来自 core/preferences 数据模块的 toggle() 操作。你需要知道编辑器名称(core/edit-sitecore/edit-post)和偏好名称。在本例中,showIconLabels 是你将针对的偏好。

将此代码添加到你的插件 src/index.js 文件中:

dispatch( commandsStore ).registerCommand( {
	name:  'dev-blog/toggle-button-labels',
	label: __( 'Toggle button labels', 'dev-blog' ),
	icon:  button,
	context: 'site-editor-edit',
	callback: ( { close } ) => {
		
		// Toggles preference for site editor.
		if ( undefined !== wp.editSite ) {
			dispatch( 'core/preferences' ).toggle(
				'core/edit-site',
				'showIconLabels'
			);
		}
		// Toggles preference for post editor.
		else if ( undefined !== wp.editPost ) {
			dispatch( 'core/preferences' ).toggle(
				'core/edit-post',
				'showIconLabels'
			);
		}
		close();
	}
} );

是否实现条件语句完全取决于你。你可能认为更好的用户体验是切换两个编辑器的此偏好,无论用户当前使用的是哪一个。你仍然需要两个 dispatch( 'core/preferences' ).toggle() 调用,但可以删除它们周围的条件检查。

示例 #4:在组件内工作

前面的示例向你展示了如何从组件外部添加一些简单的命令。但你通常会在插件的组件内工作。

让我们更进一步,改进“示例 #2:切换面板命令”部分中的一些粗糙之处。

特别是,你将把命令包装在一个组件内。你还将通过显示条件标签和在面板状态更改时在屏幕左下角触发 snackbar 通知消息来改善用户体验:

WordPress 文章编辑器,屏幕上覆盖着命令面板模态框。显示了用于切换讨论面板的搜索。

首先,你需要更新导入以包含其他一些项目:

  • 来自 @wordpress/commands 包的 useCommand
  • 来自 @wordpress/data 包的 useDispatchuseSelect
  • 来自 @wordpress/buttons 包的 registerPlugin

更新 index.js 中的导入,使其看起来像这样:

import { store, useCommand } from '@wordpress/commands';
import { dispatch, useDispatch, useSelect } from '@wordpress/data';
import { __ } from '@wordpress/i18n';
import { settings, search, comment, button } from '@wordpress/icons';
import { registerPlugin } from '@wordpress/plugins';

现在将此代码添加到你的 index.js 文件中:

if ( undefined !== wp.editPost ) {
	registerPlugin( 'dev-blog-command-palette', {
		render: () => {
			// Determine if the discussion panel is enabled.
			const discussionPanelEnabled = useSelect( ( select ) => {
				return select( 'core/edit-post' ).isEditorPanelEnabled( 
					'discussion-panel' 
				);
			}, [] );

			// Get functions for toggling panels and creating snackbars.
			const { toggleEditorPanelEnabled } = useDispatch( 'core/edit-post' );
			const { createInfoNotice }         = useDispatch( 'core/notices'   );

			// Register command to toggle discussion panel.
			useCommand( {
				name:  'dev-blog/discussion-show-hide',
				label: discussionPanelEnabled
				       ? __( 'Hide discussion panel', 'dev-blog' ) 
				       : __( 'Show discussion panel', 'dev-blog' ),
				icon:  comment,
				callback: ( { close } ) => {
					// Toggle the discussion panel.
					toggleEditorPanelEnabled( 'discussion-panel' );
					
					// Add a snackbar notice.
					createInfoNotice(
						discussionPanelEnabled
						? __( 'Discussion panel hidden.', 'dev-blog' )
						: __( 'Discussion panel displayed.', 'dev-blog' ),
						{
							id:   'dev-blog/toggle-discussion/notice',
							type: 'snackbar'
						}
					);
		
					close();
				}
			} );
		}
	} );
}

这些代码的许多内容可能与之前的示例不同。以下是每个函数或钩子调用的简要说明:

registerPlugin():用作渲染组件的包装器。
useCommand():注册显示或隐藏讨论面板的命令。
useSelect():从存储中选择数据:core/edit-post
isEditorPanelEnabled():决定讨论小组是否启用。
useDispatch():从和得到作用函数:core/edit-postcore/notices
toggleEditorPanelEnabled():切换讨论面板。
createInfoNotice():制造小吃吧的广告。
现在你应该拥有了开始使用命令调色板API所需的一切。我很想听听你想用它做什么,欢迎在评论区分享你的想法。