### 步骤三：注册元字段

若要在 `post_meta` 表中操作字段，请使用 [register_post_meta](https://developer.wordpress.org/reference/functions/register_post_meta/) 函数创建名为 `sidebar_plugin_meta_block_field` 的新字段。

注意：此字段需对 REST API 可用，因为区块编辑器通过该接口访问数据。

将以下 PHP 代码添加到插件的 `init` 回调函数中：

```php
register_post_meta( 'post', 'sidebar_plugin_meta_block_field', array(
	'show_in_rest' => true,
	'single' => true,
	'type' => 'string',
) );
```

可通过查询区块编辑器存储空间来验证字段是否加载成功。实现后，重新加载编辑器页面并打开浏览器开发者控制台，在控制台中执行以下 JavaScript 代码片段进行确认：

```js
wp.data.select( 'core/editor' ).getCurrentPost().meta;
```

该函数将返回包含已注册元字段的对象。

若代码返回 `undefined`，请确保文章类型支持 `custom-fields`。可通过[注册文章类型](https://developer.wordpress.org/reference/functions/register_post_type/#supports)时设置，或使用 [add_post_type_support 函数](https://developer.wordpress.org/reference/functions/add_post_type_support/)实现。

### 步骤四：初始化输入控件

当字段在编辑器存储中就绪后，即可将其呈现在用户界面中。我们将输入控件提取为独立函数以保持代码整洁，便于后续功能扩展。

```js
( function ( wp ) {
	var el = React.createElement;
	var registerPlugin = wp.plugins.registerPlugin;
	var PluginSidebar = wp.editor.PluginSidebar;
	var TextControl = wp.components.TextControl;

	var MetaBlockField = function () {
		return el( TextControl, {
			label: '元区块字段',
			value: '初始值',
			onChange: function ( content ) {
				console.log( '内容已更改为 ', content );
			},
		} );
	};

	registerPlugin( 'my-plugin-sidebar', {
		render: function () {
			return el(
				PluginSidebar,
				{
					name: 'my-plugin-sidebar',
					icon: 'admin-post',
					title: '我的插件侧边栏',
				},
				el(
					'div',
					{ className: 'plugin-sidebar-content' },
					el( MetaBlockField )
				)
			);
		},
	} );
} )( window.wp );
```

我们需要将 `MetaBlockField` 组件的初始值设为 `sidebar_plugin_meta_block_field` 的值，并在该值变化时保持同步更新。

`useSelect` 函数用于在组件加载时获取数据，并在数据变更时自动更新。以下是使用 `useSelect` 的代码更新版本：

```js
( function ( wp ) {
	var el = React.createElement;
	var registerPlugin = wp.plugins.registerPlugin;
	var PluginSidebar = wp.editor.PluginSidebar;
	var Text = wp.components.TextControl;
	var useSelect = wp.data.useSelect;

	var MetaBlockField = function () {
		var metaFieldValue = useSelect( function ( select ) {
			return select( 'core/editor' ).getEditedPostAttribute(
				'meta'
			)[ 'sidebar_plugin_meta_block_field' ];
		}, [] );

		return el( Text, {
			label: '元区块字段',
			value: metaFieldValue,
			onChange: function ( content ) {
				console.log( '内容已更改为 ', content );
			},
		} );
	};

	registerPlugin( 'my-plugin-sidebar', {
		render: function () {
			return el(
				PluginSidebar,
				{
					name: 'my-plugin-sidebar',
					icon: 'admin-post',
					title: '我的插件侧边栏',
				},
				el(
					'div',
					{ className: 'plugin-sidebar-content' },
					el( MetaBlockField )
				)
			);
		},
	} );
} )( window.wp );
```

`wp.data.useSelect` 函数来自 `@wordpress/data` 包，因此需要在 PHP 的 `wp_register_script` 函数中将 `wp-data` 添加为依赖项。

注意：`getEditedPostAttribute` 调用用于获取文章的最新值，包括用户尚未保存的编辑内容。

更新代码后重新加载并打开侧边栏即可验证功能。此时输入框内容不再是「初始值」而显示为空字符串。虽然用户暂时还无法输入值，但可以验证当存储中的值发生变化时组件是否会更新。打开浏览器控制台执行：

```js
wp.data
	.dispatch( 'core/editor' )
	.editPost( { meta: { sidebar_plugin_meta_block_field: 'hello world!' } } );
```

即可观察到输入组件中的内容实时更新。

### 步骤5：在输入内容变化时更新元字段

最后一步是在输入内容发生变化时更新元字段。
`useDispatch` 函数接收一个存储名称作为唯一参数，并返回可用于更新存储的方法，这里我们将使用 `editPost`。

```js
( function ( wp ) {
	var el = React.createElement;
	var registerPlugin = wp.plugins.registerPlugin;
	var PluginSidebar = wp.editor.PluginSidebar;
	var TextControl = wp.components.TextControl;
	var useSelect = wp.data.useSelect;
	var useDispatch = wp.data.useDispatch;

	var MetaBlockField = function ( props ) {
		var metaFieldValue = useSelect( function ( select ) {
			return select( 'core/editor' ).getEditedPostAttribute(
				'meta'
			)[ 'sidebar_plugin_meta_block_field' ];
		}, [] );

		var editPost = useDispatch( 'core/editor' ).editPost;

		return el( TextControl, {
			label: '元区块字段',
			value: metaFieldValue,
			onChange: function ( content ) {
				editPost( {
					meta: { sidebar_plugin_meta_block_field: content },
				} );
			},
		} );
	};

	registerPlugin( 'my-plugin-sidebar', {
		render: function () {
			return el(
				PluginSidebar,
				{
					name: 'my-plugin-sidebar',
					icon: 'admin-post',
					title: '我的插件侧边栏',
				},
				el(
					'div',
					{ className: 'plugin-sidebar-content' },
					el( MetaBlockField )
				)
			);
		},
	} );
} )( window.wp );
```

更新后，当用户输入时，输入控件会调用 `editPost` 并在每次按键时更新编辑器存储。

更新 JavaScript 代码，加载侧边栏并在输入框中输入内容。您可以通过在输入控件中输入内容并在浏览器的开发控制台中执行以下 JavaScript 代码片段来确认内容已保存：

```js
wp.data.select( 'core/editor' ).getEditedPostAttribute( 'meta' )[
	'sidebar_plugin_meta_block_field'
];
```

显示的消息应该是您在输入框中输入的内容。

在保存文章时，您可以通过保存后重新加载页面并确认输入控件已初始化为您最后输入的值，来验证内容是否正确存储到数据库中。

## 附加资源

有关使用 [@wordpress/data 包](/packages/data/README.md) 的文档。

本指南中使用的函数：

-   [useSelect](/packages/data/README.md#useselect)
-   [getEditedPostAttribute](/docs/reference-guides/data/data-core-editor.md#geteditedpostattribute)
-   [useDispatch](/packages/data/README.md#usedispatch)

## 总结

您现在拥有一个自定义侧边栏，可用于更新 `post_meta` 内容。

完整示例可在 [block-development-examples](https://github.com/WordPress/block-development-examples) 代码库中下载 [plugin-sidebar 示例](https://github.com/WordPress/block-development-examples/tree/trunk/plugins/plugin-sidebar-9ee4a6)。

### 注意

如果您在编辑器“偏好设置”的“面板”页面中启用了“自定义字段”（通过右上角的三个点），与 TextControl 同名的字段（在本例中为 `sidebar_plugin_meta_block_field`）也会出现在编辑器窗口底部的自定义字段面板中。这两个字段可以访问相同的元属性。

![文本控件与自定义字段](https://raw.githubusercontent.com/WordPress/gutenberg/HEAD/docs/assets/plugin-sidebar-text-control-custom-field.png)

在保存文章时，TextControl 中的值会先保存，而自定义字段中的值会后保存，因此最终保存到数据库的是自定义字段中的值。因此，如果您更改了 TextControl 中的值，最终保存的仍然是自定义字段中的值。

如果未启用自定义字段，则不会出现此问题。

如果您需要启用自定义字段并在侧边栏中显示文章元数据，有两种可能的解决方案：

1. 在元字段名称前添加下划线，例如上述示例中的名称改为 `_sidebar_plugin_meta_block_field`。这表示该文章元数据应视为私有，不会在文章的自定义字段部分显示。使用此解决方案时，除非您在传递给 `register_post_meta` 的 `args` 数组中添加一个 `auth_callback` 属性，并提供一个最终返回 `true` 的函数，否则在保存文章时会生成错误。有关更多信息，请参阅 [post_meta](https://developer.wordpress.org/reference/functions/register_meta/#parameters) 页面中的 `args` 文档。
2. 在 TextControl 的 `onChange` 函数中，将目标指向值字段的文本区域，并将其值设置为与 TextControl 元字段中的值相同。这样，两个位置的值将保持一致，因此您可以确保即使更改了 TextControl 中的值，它仍然会被保存到数据库中。

```js
return el( TextControl, {
  label: '元区块字段',
  value: metaFieldValue,
  onChange: function ( content ) {
    editPost( {
      meta: { sidebar_plugin_meta_block_field: content }
    })
    document.querySelector( {值字段文本区域} ).innerHTML = content;
  },
} );
```

# 插件侧边栏

## 概述

如何为插件添加侧边栏。侧边栏位于编辑器最右侧区域。您的插件可以在检查器控件（齿轮图标）旁添加额外图标，该图标可展开显示。

![侧边栏示例](https://raw.githubusercontent.com/WordPress/gutenberg/HEAD/docs/assets/sidebar-up-and-running.png)

*注意：本教程主要讲解自定义侧边栏，如需在侧边栏添加控件请参阅[区块工具栏与设置侧边栏](/docs/getting-started/fundamentals/block-in-the-editor.md)*

## 准备工作

本教程假设您已具备插件基础环境，并准备添加PHP和JavaScript代码。请先参阅[JavaScript入门指南](/docs/getting-started/fundamentals/javascript-in-the-block-editor.md)了解WordPress插件基础知识及如何使用JavaScript扩展区块编辑器。

## 分步指南

### 步骤1：启动侧边栏

首先需要告知编辑器存在包含独立侧边栏的新插件。请分别使用`@wordpress/plugins`、`@wordpress/editor`和`react`包提供的[registerPlugin](/packages/plugins/README.md)、[PluginSidebar](/packages/editor/README.md#pluginsidebar)和[createElement](/packages/element/README.md)工具。

将以下代码添加至名为`plugin-sidebar.js`的JavaScript文件，并保存到插件目录：

```js
( function ( wp, React ) {
	var el = React.createElement;
	var registerPlugin = wp.plugins.registerPlugin;
	var PluginSidebar = wp.editor.PluginSidebar;

	registerPlugin( 'my-plugin-sidebar', {
		render: function () {
			return el(
				PluginSidebar,
				{
					name: 'my-plugin-sidebar',
					icon: 'admin-post',
					title: '我的插件侧边栏',
				},
				'元字段'
			);
		},
	} );
} )( window.wp, window.React );
```

为使代码正常运行，需确保这些工具在浏览器中可用，因此必须将`wp-plugins`、`wp-editor`和`react`指定为脚本依赖项。

以下是注册脚本并声明依赖项的PHP代码：

```php
<?php

/*
插件名称：侧边栏插件
*/

function sidebar_plugin_register() {
	wp_register_script(
		'plugin-sidebar-js',
		plugins_url( 'plugin-sidebar.js', __FILE__ ),
		array( 'wp-plugins', 'wp-editor', 'react' )
	);
}
add_action( 'init', 'sidebar_plugin_register' );

function sidebar_plugin_script_enqueue() {
	wp_enqueue_script( 'plugin-sidebar-js' );
}
add_action( 'enqueue_block_editor_assets', 'sidebar_plugin_script_enqueue' );
```

安装并启用此插件后，编辑器右上角将出现新的图钉图标。点击即可展开插件侧边栏：

![运行中的侧边栏](https://raw.githubusercontent.com/WordPress/gutenberg/HEAD/docs/assets/sidebar-up-and-running.png)

### 步骤2：调整样式并添加控件

侧边栏运行后，接下来需要填充必要组件和基础样式。

为可视化编辑元字段值，将使用输入组件。`@wordpress/components`包提供多种可复用组件，其中[TextControl](/packages/components/src/text-control/README.md)专用于创建输入字段：

```js
( function ( wp ) {
	var el = React.createElement;
	var registerPlugin = wp.plugins.registerPlugin;
	var PluginSidebar = wp.editor.PluginSidebar;
	var TextControl = wp.components.TextControl;

	registerPlugin( 'my-plugin-sidebar', {
		render: function () {
			return el(
				PluginSidebar,
				{
					name: 'my-plugin-sidebar',
					icon: 'admin-post',
					title: '我的插件侧边栏',
				},
				el(
					'div',
					{ className: 'plugin-sidebar-content' },
					el( TextControl, {
						label: '元区块字段',
						value: '初始值',
						onChange: function ( content ) {
							console.log( '内容已更改为 ', content );
						},
					} )
				)
			);
		},
	} );
} )( window.wp );
```

使用新代码更新`plugin-sidebar.js`。注意代码使用了`@wordpress/components`包中的`wp.components`工具，请确保在PHP文件的`wp_register_script`函数中添加`wp-components`依赖项。

代码新增内容：
- 为`div`元素添加CSS类`plugin-sidebar-content`用于样式定位
- 使用`TextControl`组件替代纯文本'元字段'

通过新增的CSS类可添加基础样式。在插件目录创建`plugin-sidebar.css`文件并添加内边距设置：

```css
.plugin-sidebar-content {
	padding: 16px;
}
```

注册样式表并通过`enqueue_block_editor_assets`与JavaScript文件同时加载。

完成修改后，PHP代码如下所示：

```php
<?php

/*
插件名称：侧边栏示例
*/

function sidebar_plugin_register() {
	wp_register_script(
		'plugin-sidebar-js',
		plugins_url( 'plugin-sidebar.js', __FILE__ ),
		array(
			'react',
			'wp-plugins',
			'wp-editor',
			'wp-components'
		)
	);
	wp_register_style(
		'plugin-sidebar-css',
		plugins_url( 'plugin-sidebar.css', __FILE__ )
	);
}
add_action( 'init', 'sidebar_plugin_register' );

function sidebar_plugin_script_enqueue() {
	wp_enqueue_script( 'plugin-sidebar-js' );
	wp_enqueue_style( 'plugin-sidebar-css' );
}
add_action( 'enqueue_block_editor_assets', 'sidebar_plugin_script_enqueue' );
```

重新加载编辑器并打开侧边栏：

![含样式和控件的侧边栏](https://raw.githubusercontent.com/WordPress/gutenberg/HEAD/docs/assets/sidebar-style-and-controls.png)

当前代码尚未实现数据存储与检索功能，后续步骤将重点介绍如何连接元区块字段。