docs
This commit is contained in:
11
docs/how-to-guides/block-tutorial/README.md
Normal file
11
docs/how-to-guides/block-tutorial/README.md
Normal file
@@ -0,0 +1,11 @@
|
||||
# 区块
|
||||
|
||||
本教程旨在逐步讲解创建新区块类型的基础知识。从最简单的示例开始,每个新章节将逐步扩展内容,涵盖您在实现自己的区块类型时可能需要使用的常见功能。
|
||||
|
||||
要跟随本教程学习,您可以下载[配套的 WordPress 插件](https://github.com/WordPress/block-development-examples),其中包含所有示例供您在自己的站点上尝试。在每个步骤中,可以通过修改示例来实践自己的想法,并观察它们对区块行为产生的影响。
|
||||
|
||||
> 要获取最新版本的 .zip 文件,请前往代码库的[发布页面](https://github.com/WordPress/block-development-examples/releases),在最新发布的「资源」部分查找。
|
||||
|
||||
代码片段提供两种格式:「JSX」和「原生」。JSX 指使用 JSX 语法的 JavaScript 代码,需要构建步骤。原生指无需构建的「经典」JavaScript。您可以通过每个代码示例上方的选项卡切换这两种格式。使用 JSX 需要您运行 [JavaScript 构建步骤](/docs/how-to-guides/javascript/js-build-setup/),将代码编译为浏览器兼容的格式。
|
||||
|
||||
请注意,创建区块或扩展编辑器并不强制要求使用 JSX,您可以使用经典 JavaScript。然而,一旦熟悉了 JSX 和构建步骤,许多开发者往往会发现它更易于阅读和编写,因此您看到的大多数代码示例都使用 JSX 语法。
|
||||
@@ -0,0 +1,163 @@
|
||||
# 使用样式与样式表
|
||||
|
||||
## 概述
|
||||
|
||||
区块通常会在文章内容中插入需要特定样式化的标记(HTML)。本指南将详细介绍几种在区块编辑器中使用 CSS 的不同方法,以及如何处理样式和样式表。
|
||||
|
||||
## 开始之前
|
||||
|
||||
您需要准备一个基础区块和 WordPress 开发环境来实现本指南中的示例。请参阅[快速入门指南](/docs/getting-started/quick-start-guide.md)或[区块教程](/docs/getting-started/tutorial.md)完成环境配置。
|
||||
|
||||
## 添加样式的方法
|
||||
|
||||
以下是为区块添加样式的不同方法(适用于编辑器界面或保存后的显示效果):
|
||||
|
||||
## 方法一:内联样式
|
||||
|
||||
第一种方法演示了如何添加内联样式。这种方式会将定义的样式转换为插入元素的属性。
|
||||
|
||||
通过 `useBlockProps` React 钩子可以设置并应用区块包装元素的属性。具体示例如下:
|
||||
|
||||
```jsx
|
||||
import { registerBlockType } from '@wordpress/blocks';
|
||||
import { useBlockProps } from '@wordpress/block-editor';
|
||||
|
||||
registerBlockType( 'gutenberg-examples/example-02-stylesheets', {
|
||||
edit() {
|
||||
const greenBackground = {
|
||||
backgroundColor: '#090',
|
||||
color: '#fff',
|
||||
padding: '20px',
|
||||
};
|
||||
|
||||
const blockProps = useBlockProps( { style: greenBackground } );
|
||||
|
||||
return (
|
||||
<p { ...blockProps }>Hello World(编辑器界面显示,绿色背景)。</p>
|
||||
);
|
||||
},
|
||||
save() {
|
||||
const redBackground = {
|
||||
backgroundColor: '#900',
|
||||
color: '#fff',
|
||||
padding: '20px',
|
||||
};
|
||||
|
||||
const blockProps = useBlockProps.save( { style: redBackground } );
|
||||
|
||||
return (
|
||||
<p { ...blockProps }>Hello World(前端显示,红色背景)。</p>
|
||||
);
|
||||
},
|
||||
} );
|
||||
```
|
||||
|
||||
## 方法二:区块类名
|
||||
|
||||
内联样式适用于少量 CSS 的情况。如果需要更复杂的样式,使用独立的样式表文件会更便于管理。
|
||||
|
||||
`useBlockProps` 钩子会自动包含区块的类名,它会根据区块名称生成以 `wp-block-` 为前缀的类名,并将命名空间分隔符 `/` 替换为 `-`。
|
||||
|
||||
例如区块名称 `gutenberg-examples/example-02-stylesheets` 对应的类名为:`wp-block-gutenberg-examples-example-02-stylesheets`。虽然较长但能有效避免与其他区块冲突。
|
||||
|
||||
```jsx
|
||||
import { registerBlockType } from '@wordpress/blocks';
|
||||
import { useBlockProps } from '@wordpress/block-editor';
|
||||
|
||||
registerBlockType( 'gutenberg-examples/example-02-stylesheets', {
|
||||
edit() {
|
||||
const blockProps = useBlockProps();
|
||||
|
||||
return (
|
||||
<p { ...blockProps }>Hello World(编辑器界面显示,绿色背景)。</p>
|
||||
);
|
||||
},
|
||||
save() {
|
||||
const blockProps = useBlockProps.save();
|
||||
|
||||
return (
|
||||
<p { ...blockProps }>Hello World(前端显示,红色背景)。</p>
|
||||
);
|
||||
},
|
||||
} );
|
||||
```
|
||||
|
||||
### 构建或添加依赖
|
||||
|
||||
如需将 blockEditor 添加为依赖项,请确保执行构建步骤或更新资源 PHP 文件。
|
||||
|
||||
构建脚本并更新用于跟踪依赖关系和构建版本的资源文件:
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
|
||||
### 加载样式表
|
||||
|
||||
与脚本类似,您可以通过 `block.json` 文件加载区块的样式表。
|
||||
|
||||
- `editorStyle` 属性:指定仅在编辑器界面加载的 CSS 文件
|
||||
- `style` 属性:指定在编辑器界面和前端都会加载的 CSS 文件
|
||||
- `viewStyle` 属性:指定仅在前端加载的 CSS 文件(当区块被使用时)
|
||||
|
||||
值得注意的是,如果编辑器内容位于 iframe 中,`style` 和 `editorStyle` 都会在 iframe 内加载。`editorStyle` 还会在 iframe 外加载,因此可用于编辑器内容和界面样式。
|
||||
|
||||
示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"apiVersion": 3,
|
||||
"name": "gutenberg-examples/example-02-stylesheets",
|
||||
"title": "示例:样式表",
|
||||
"icon": "universal-access-alt",
|
||||
"category": "layout",
|
||||
"editorScript": "file:./block.js",
|
||||
"editorStyle": "file:./editor.css",
|
||||
"style": "file:./style.css"
|
||||
}
|
||||
```
|
||||
|
||||
在插件目录中创建 `editor.css` 文件用于编辑器界面:
|
||||
|
||||
```css
|
||||
/* 绿色背景 */
|
||||
.wp-block-gutenberg-examples-example-02-stylesheets {
|
||||
background: #090;
|
||||
color: white;
|
||||
padding: 20px;
|
||||
}
|
||||
```
|
||||
|
||||
创建 `style.css` 文件用于前端显示:
|
||||
|
||||
```css
|
||||
/* 红色背景 */
|
||||
.wp-block-gutenberg-examples-example-02-stylesheets {
|
||||
background: #900;
|
||||
color: white;
|
||||
padding: 20px;
|
||||
}
|
||||
```
|
||||
|
||||
在 block.json 中指定后,这些文件将自动加载。
|
||||
|
||||
<div class="callout callout-info">
|
||||
|
||||
如果使用 `@wordpress/scripts`,需要在对应的 JavaScript 文件中导入样式表,以便 `@wordpress/scripts` 处理样式表。
|
||||
|
||||
示例:
|
||||
|
||||
- 在 `edit.js` 中添加 `import './editor.scss';`
|
||||
- 在 `index.js` 中添加 `import './style.scss';`
|
||||
- 在 `view.js` 中添加 `import './view.scss';`(交互式区块模板)
|
||||
</div>
|
||||
|
||||
**注意:** 如需加载多个文件,可以像其他插件或主题一样使用标准的 `wp_enqueue_style` 函数。区块编辑器建议使用以下钩子:
|
||||
|
||||
- `enqueue_block_editor_assets` - 仅在编辑器界面加载
|
||||
- `enqueue_block_assets` - 在前端和编辑器界面同时加载
|
||||
|
||||
## 总结
|
||||
|
||||
本指南展示了通过内联样式或独立样式表为区块应用样式的不同方法。这两种方法都使用了 `useBlockProps` 钩子,更多细节请参阅[区块包装器参考文档](/docs/reference-guides/block-api/block-edit-save.md#block-wrapper-props)。
|
||||
|
||||
完整代码请查看[区块开发示例库](https://github.com/WordPress/block-development-examples)中的 [stylesheets-79a4c3](https://github.com/WordPress/block-development-examples/tree/trunk/plugins/stylesheets-79a4c3) 示例。
|
||||
137
docs/how-to-guides/block-tutorial/creating-dynamic-blocks.md
Normal file
137
docs/how-to-guides/block-tutorial/creating-dynamic-blocks.md
Normal file
@@ -0,0 +1,137 @@
|
||||
# 创建动态区块
|
||||
|
||||
动态区块是在前端渲染时实时构建结构和内容的区块。
|
||||
|
||||
动态区块主要有两个用途:
|
||||
|
||||
1. **内容需要实时更新的区块**:即使文章未更新,区块内容也会自动变化。WordPress 自带的「最新文章」区块就是典型示例——每当发布新文章时,所有使用该区块的位置都会同步更新。
|
||||
2. **需要即时呈现代码更新的区块**:当修改区块代码(HTML/CSS/JS)时,使用动态区块可确保修改立即在全站所有该区块实例上生效。(若不使用动态区块,Gutenberg 的[验证流程](/docs/reference-guides/block-api/block-edit-save.md#validation)会触发,导致用户看到“此区块似乎已被外部修改”的提示)
|
||||
|
||||
对于多数动态区块,`save` 回调函数应返回 `null`,这会让编辑器仅将[区块属性](/docs/reference-guides/block-api/block-attributes.md)保存至数据库。这些属性随后会传入服务端渲染回调,从而由开发者决定如何在前端展示区块。返回 `null` 时,编辑器会跳过区块标记验证流程,避免因频繁变化的标记引发问题。
|
||||
|
||||
若在动态区块中使用 [InnerBlocks](/docs/how-to-guides/block-tutorial/nested-blocks-inner-blocks.md),需通过 `<InnerBlocks.Content/>` 在 `save` 回调中保存内部区块内容。
|
||||
|
||||
也可保存区块的 HTML 表示形式。若设置了服务端渲染回调,该 HTML 会被回调输出替换;但当区块被停用或渲染回调被移除时,此 HTML 仍会正常渲染。
|
||||
|
||||
区块属性可用于保存任何需要存储的内容或设置。例如最新文章区块中,可将要显示的文章数量保存为属性;又或者针对需要在前端展示的每项内容(如标题文本、段落文字、图片、链接等),都可通过属性进行配置。
|
||||
|
||||
以下代码示例展示了如何创建仅显示最新文章链接的动态区块:
|
||||
|
||||
```jsx
|
||||
import { registerBlockType } from '@wordpress/blocks';
|
||||
import { useSelect } from '@wordpress/data';
|
||||
import { useBlockProps } from '@wordpress/block-editor';
|
||||
|
||||
registerBlockType( 'gutenberg-examples/example-dynamic', {
|
||||
apiVersion: 3,
|
||||
title: '示例:最新文章',
|
||||
icon: 'megaphone',
|
||||
category: 'widgets',
|
||||
|
||||
edit: () => {
|
||||
const blockProps = useBlockProps();
|
||||
const posts = useSelect( ( select ) => {
|
||||
return select( 'core' ).getEntityRecords( 'postType', 'post' );
|
||||
}, [] );
|
||||
|
||||
return (
|
||||
<div { ...blockProps }>
|
||||
{ ! posts && '加载中' }
|
||||
{ posts && posts.length === 0 && '暂无文章' }
|
||||
{ posts && posts.length > 0 && (
|
||||
<a href={ posts[ 0 ].link }>
|
||||
{ posts[ 0 ].title.rendered }
|
||||
</a>
|
||||
) }
|
||||
</div>
|
||||
);
|
||||
},
|
||||
} );
|
||||
```
|
||||
|
||||
由于是动态区块,无需在客户端重写默认的 `save` 实现,但需要配置服务端组件。前端显示内容取决于 `register_block_type` 的 `render_callback` 属性所调用的函数。
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
/**
|
||||
* 插件名称:Gutenberg 动态区块示例
|
||||
*/
|
||||
|
||||
function gutenberg_examples_dynamic_render_callback( $block_attributes, $content ) {
|
||||
$recent_posts = wp_get_recent_posts( array(
|
||||
'numberposts' => 1,
|
||||
'post_status' => 'publish',
|
||||
) );
|
||||
if ( count( $recent_posts ) === 0 ) {
|
||||
return '暂无文章';
|
||||
}
|
||||
$post = $recent_posts[ 0 ];
|
||||
$post_id = $post['ID'];
|
||||
return sprintf(
|
||||
'<a class="wp-block-my-plugin-latest-post" href="%1$s">%2$s</a>',
|
||||
esc_url( get_permalink( $post_id ) ),
|
||||
esc_html( get_the_title( $post_id ) )
|
||||
);
|
||||
}
|
||||
|
||||
function gutenberg_examples_dynamic() {
|
||||
// 自动加载依赖项与版本号
|
||||
$asset_file = include( plugin_dir_path( __FILE__ ) . 'build/index.asset.php');
|
||||
|
||||
wp_register_script(
|
||||
'gutenberg-examples-dynamic',
|
||||
plugins_url( 'build/block.js', __FILE__ ),
|
||||
$asset_file['dependencies'],
|
||||
$asset_file['version']
|
||||
);
|
||||
|
||||
register_block_type( 'gutenberg-examples/example-dynamic', array(
|
||||
'api_version' => 3,
|
||||
'editor_script' => 'gutenberg-examples-dynamic',
|
||||
'render_callback' => 'gutenberg_examples_dynamic_render_callback'
|
||||
) );
|
||||
|
||||
}
|
||||
add_action( 'init', 'gutenberg_examples_dynamic' );
|
||||
```
|
||||
|
||||
需要注意以下几点:
|
||||
- `edit` 函数仍在编辑器中展示区块预览(可与渲染版本完全不同,由区块作者决定)
|
||||
- 内置 `save` 函数直接返回 `null`,因为渲染在服务端完成
|
||||
- 服务端渲染函数接收区块属性及内部内容作为参数,返回标记(与短代码机制高度相似)
|
||||
|
||||
**注意:** 关于颜色、边框、间距等常见自定义设置,我们将在[下一章](/docs/how-to-guides/block-tutorial/block-supports-in-dynamic-blocks.md)了解如何通过区块支持功能高效实现。
|
||||
|
||||
## 在区块编辑器中实时渲染
|
||||
|
||||
Gutenberg 2.8 版本引入了 [`<ServerSideRender>`](/packages/server-side-render/README.md) 区块,支持在服务端使用 PHP 进行渲染而非 JavaScript。
|
||||
|
||||
*服务端渲染仅作为降级方案,始终推荐使用客户端 JavaScript 渲染(速度更快且支持更灵活的编辑器操作)。*
|
||||
|
||||
```jsx
|
||||
import { registerBlockType } from '@wordpress/blocks';
|
||||
import ServerSideRender from '@wordpress/server-side-render';
|
||||
import { useBlockProps } from '@wordpress/block-editor';
|
||||
|
||||
registerBlockType( 'gutenberg-examples/example-dynamic', {
|
||||
apiVersion: 3,
|
||||
title: '示例:最新文章',
|
||||
icon: 'megaphone',
|
||||
category: 'widgets',
|
||||
|
||||
edit: function ( props ) {
|
||||
const blockProps = useBlockProps();
|
||||
return (
|
||||
<div { ...blockProps }>
|
||||
<ServerSideRender
|
||||
block="gutenberg-examples/example-dynamic"
|
||||
attributes={ props.attributes }
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
} );
|
||||
```
|
||||
|
||||
注意此代码使用了 `wp-server-side-render` 包但未使用 `wp-data`,请确保在 PHP 代码中更新依赖项。建议使用 wp-scripts 自动构建依赖(具体 PHP 代码配置可参考 [block-development-examples 代码库](https://github.com/WordPress/block-development-examples/tree/trunk/plugins/basic-esnext-a2ab62))。
|
||||
@@ -0,0 +1,286 @@
|
||||
### 在前端实现自定义查询功能
|
||||
|
||||
查询循环区块主要通过文章模板区块运作,该区块接收属性并据此构建查询。查询循环区块的其他一级子元素(如分页区块)也以相同方式工作。它们构建查询后,通过 [`query_loop_block_query_vars`](https://developer.wordpress.org/reference/hooks/query_loop_block_query_vars/) 过滤器暴露结果。
|
||||
|
||||
您可以通过挂接该过滤器来修改查询。请务必确保不会对其他查询循环区块产生副作用,至少需要检查仅将过滤器应用于您的变体!
|
||||
|
||||
```php
|
||||
if( 'my-plugin/books-list' === $block[ 'attrs' ][ 'namespace' ] ) {
|
||||
add_filter(
|
||||
'query_loop_block_query_vars',
|
||||
function( $query ) {
|
||||
/** 在此处读取区块的自定义查询参数并构建查询 */
|
||||
},
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
(上述代码中,我们假设您有某种访问区块的方式,例如通过 [`pre_render_block`](https://developer.wordpress.org/reference/hooks/pre_render_block/) 过滤器,但具体解决方案可能因使用场景而异,因此这并非硬性建议)。
|
||||
|
||||
### 在编辑器端实现自定义查询功能
|
||||
|
||||
要完成自定义变体,我们可能希望编辑器能响应自定义查询的更改,并相应显示适当的预览。虽然这对区块功能并非必需,但能为区块使用者提供完全集成的用户体验。
|
||||
|
||||
查询循环区块通过 [WordPress REST API](https://developer.wordpress.org/rest-api/) 获取文章以显示预览。任何添加到 `query` 对象的额外参数都将作为查询参数传递给 API。这意味着这些额外参数要么需要 REST API 支持,要么需要通过自定义过滤器处理,例如 [`rest_{$this->post_type}_query`](https://developer.wordpress.org/reference/hooks/rest_this-post_type_query/) 过滤器,该过滤器允许您介入自定义文章类型的任何 API 请求。如下所示:
|
||||
|
||||
```php
|
||||
add_filter(
|
||||
'rest_book_query',
|
||||
function( $args, $request ) {
|
||||
/** 我们可以从这里访问自定义参数 */
|
||||
$book_author = $request->get_param( 'bookAuthor' );
|
||||
/** ...您的自定义查询逻辑 */
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
就这样,您就创建了一个功能完整的查询循环区块变体!
|
||||
|
||||
# 扩展查询循环区块
|
||||
|
||||
查询循环区块是一个功能强大的工具,允许用户循环遍历指定的文章列表,并显示一系列将继承列表中每篇文章上下文的区块。例如,可以将其设置为循环遍历特定分类下的所有文章,并为每篇文章显示其特色图像。当然,功能远不止于此!
|
||||
|
||||
但正因为查询循环区块功能强大且支持高度自定义,它也可能令人望而生畏。大多数用户不希望面对查询循环区块的全部功能,因为他们可能不熟悉“查询”概念及其相关技术术语。相反,大多数用户可能更青睐预设版本的区块,具有更少的设置项和更清晰的命名。默认提供的“文章列表”变体就是这种做法的良好示例:用户可以在不接触技术细节的情况下使用查询循环区块,同时也更有可能发现并理解该区块的用途。
|
||||
|
||||
同样地,许多扩展开发者可能需要提供定制化区块版本的方法,这些版本具有自己的预设配置、附加设置,并剔除与其使用场景无关的自定义选项(例如,通常针对其自定义文章类型)。查询循环区块提供了非常强大的方式来创建此类变体。
|
||||
|
||||
## 通过变体扩展区块
|
||||
|
||||
通过注册具有特定查询循环区块设置的自定义区块变体,您可以更精细地控制其呈现方式,同时仍能使用查询循环区块底层提供的全部功能。如果您不熟悉区块变体,可在此处了解更多信息。
|
||||
|
||||
通过区块变体API,您可以为您的使用场景提供最合理的默认设置。
|
||||
|
||||
要使查询循环变体正常工作,我们需要:
|
||||
- 为`core/query`区块注册具有某些默认值的区块变体
|
||||
- 为区块变体定义布局
|
||||
- 在`isActive`区块变体属性中使用`namespace`属性
|
||||
|
||||
让我们以为一个注册了`book`自定义文章类型的插件设置变体为例,开始这段旅程。
|
||||
|
||||
### 1. 提供合理的默认值
|
||||
|
||||
您的第一步是创建一个变体,该变体将默认显示书籍列表而非博客文章。完整的变体代码如下所示:
|
||||
|
||||
```js
|
||||
const MY_VARIATION_NAME = 'my-plugin/books-list';
|
||||
|
||||
registerBlockVariation( 'core/query', {
|
||||
name: MY_VARIATION_NAME,
|
||||
title: '书籍列表',
|
||||
description: '显示书籍列表',
|
||||
isActive: ( { namespace, query } ) => {
|
||||
return (
|
||||
namespace === MY_VARIATION_NAME
|
||||
&& query.postType === 'book'
|
||||
);
|
||||
},
|
||||
icon: /** 此处可放置SVG图标 */,
|
||||
attributes: {
|
||||
namespace: MY_VARIATION_NAME,
|
||||
query: {
|
||||
perPage: 6,
|
||||
pages: 0,
|
||||
offset: 0,
|
||||
postType: 'book',
|
||||
order: 'desc',
|
||||
orderBy: 'date',
|
||||
author: '',
|
||||
search: '',
|
||||
exclude: [],
|
||||
sticky: '',
|
||||
inherit: false,
|
||||
},
|
||||
},
|
||||
scope: [ 'inserter' ],
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
如果这看起来很多,请不要担心,让我们逐一查看这里的属性,了解它们的存在意义和作用。
|
||||
|
||||
本质上,您可以从这样的代码开始:
|
||||
|
||||
```js
|
||||
registerBlockVariation( 'core/query', {
|
||||
name: 'my-plugin/books-list',
|
||||
attributes: {
|
||||
query: {
|
||||
/** ...更多查询设置(如需要) */
|
||||
postType: 'book',
|
||||
},
|
||||
},
|
||||
} );
|
||||
```
|
||||
|
||||
通过这种方式,用户无需从下拉菜单中选择自定义的`postType`,即可直接获得正确的配置。但您可能会问,用户如何找到并插入这个变体?好问题!要实现这一点,您应该添加:
|
||||
|
||||
```js
|
||||
{
|
||||
/** ...变体属性 */
|
||||
scope: [ 'inserter' ],
|
||||
}
|
||||
```
|
||||
|
||||
这样,当用户在编辑器中搜索时,您的区块将像任何其他区块一样显示。此时,您可能还想为变体添加自定义图标、标题和描述,如下所示:
|
||||
|
||||
```js
|
||||
{
|
||||
/** ...变体属性 */
|
||||
title: '书籍列表',
|
||||
description: '显示书籍列表',
|
||||
icon: /* 您的SVG图标在此 */,
|
||||
}
|
||||
```
|
||||
|
||||
至此,您的自定义变体将与独立区块几乎无法区分。完全为您的插件品牌化,易于发现,并作为即插即用的选项直接提供给用户。
|
||||
|
||||
然而,您的查询循环变体尚不能正常工作——我们仍需定义布局以便其正确渲染。
|
||||
|
||||
## 扩展查询功能
|
||||
|
||||
即便具备上述所有功能,您的自定义文章类型可能仍有独特需求:可能需要支持某些用于筛选和查询的自定义属性,或者某些查询参数可能无关紧要甚至完全不支持!我们在设计查询循环区块时已考虑到这类使用场景,下面来看看如何解决这个问题。
|
||||
|
||||
### 禁用无关或不支持的查询控件
|
||||
|
||||
假设您的图书数据完全未使用`sticky`属性,那么该设置对区块定制将毫无意义。为避免用户困惑并呈现清晰的交互界面,我们需要隐藏此控件。再假设您完全未使用`author`字段(该字段通常表示将文章添加到数据库的用户),而是使用自定义的`bookAuthor`字段。此时若保留`author`筛选器不仅会造成混淆,更会直接"破坏"您的查询。
|
||||
|
||||
为此,查询循环区块变体支持名为`allowedControls`的属性,该属性接收数组参数用于指定要在检查器侧边栏显示的控件键值。默认情况下会显示所有控件,但一旦设置该属性,我们就需要明确指定相关控件!
|
||||
|
||||
截至Gutenberg 14.2版本,可用控件如下:
|
||||
|
||||
- `inherit` - 显示切换开关,允许查询直接从模板继承
|
||||
- `postType` - 显示可用文章类型下拉菜单
|
||||
- `order` - 显示查询排序方式下拉菜单
|
||||
- `sticky` - 显示置顶文章处理方式下拉菜单
|
||||
- `taxQuery` - 显示当前所选文章类型的分类法筛选器
|
||||
- `author` - 显示按作者筛选查询的输入字段
|
||||
- `search` - 显示按关键词筛选查询的输入字段
|
||||
- `format` - 显示按[格式集合](https://developer.wordpress.org/advanced-administration/wordpress/post-formats/#supported-formats)筛选查询的输入字段
|
||||
- `parents` - 显示使用父实体筛选查询的输入字段
|
||||
|
||||
在本案例中,该属性配置如下:
|
||||
|
||||
```js
|
||||
{
|
||||
/** ...变体属性 */
|
||||
allowedControls: [ 'inherit', 'order', 'taxQuery', 'search' ],
|
||||
}
|
||||
```
|
||||
|
||||
如需隐藏所有可用控件,可将`allowedControls`值设为空数组。
|
||||
|
||||
注意我们同时禁用了`postType`控件。当用户选择我们的变体时,为何还要显示令人困惑的文章类型下拉菜单?更重要的是,正如稍后将介绍的,这可能会破坏我们实现自定义控件的区块功能。
|
||||
|
||||
### 添加额外控件
|
||||
|
||||
由于我们的插件需要使用自定义属性进行查询,我们需要添加专属控件来替代刚刚从核心检查器禁用的控件。这可以通过接入[区块过滤器](https://developer.wordpress.org/block-editor/reference-guides/filters/block-filters/)的[React HOC](https://reactjs.org/docs/higher-order-components.html)实现:
|
||||
|
||||
```jsx
|
||||
import { InspectorControls } from '@wordpress/block-editor';
|
||||
|
||||
export const withBookQueryControls = ( BlockEdit ) => ( props ) => {
|
||||
// 仅当属于我们的变体时才添加这些控件
|
||||
// 此处可实现自定义逻辑进行验证,类似于前述的`isActive`函数
|
||||
// 以下假设您已编写自定义的`isMyBooksVariation`函数进行处理
|
||||
return isMyBooksVariation( props ) ? (
|
||||
<>
|
||||
<BlockEdit key="edit" { ...props } />
|
||||
<InspectorControls>
|
||||
<BookAuthorSelector /> { /** 我们的自定义组件 */ }
|
||||
</InspectorControls>
|
||||
</>
|
||||
) : (
|
||||
<BlockEdit key="edit" { ...props } />
|
||||
);
|
||||
};
|
||||
|
||||
addFilter( 'editor.BlockEdit', 'core/query', withBookQueryControls );
|
||||
```
|
||||
|
||||
当然,您需要自行实现控件的逻辑(建议参考[`@wordpress/components`](https://www.npmjs.com/package/@wordpress/components)使控件完美融入Gutenberg界面)。通过少量额外工作,您在区块属性的`query`对象内设置的任何额外参数,都可用于创建符合需求的自定义查询。
|
||||
|
||||
当前您可能需要实现略有差异的路径,以确保在前端(即最终用户侧)正确执行查询,并在编辑器端显示正确的预览。
|
||||
|
||||
```js
|
||||
{
|
||||
/** ...变体属性 */
|
||||
attributes: {
|
||||
/** ...变体属性 */
|
||||
query: {
|
||||
/** ...更多查询设置(如需要) */
|
||||
postType: 'book',
|
||||
/** 我们的自定义查询参数 */
|
||||
bookAuthor: 'J. R. R. Tolkien'
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 自定义变体布局
|
||||
|
||||
请注意,查询循环区块支持在 `scope` 属性中使用字符串 `'block'`。理论上,这是为了让变体在插入区块后被识别。更多关于区块变体选择器的信息请[参阅此处](https://github.com/WordPress/gutenberg/blob/HEAD/packages/block-editor/src/components/block-variation-picker/README.md)。
|
||||
|
||||
然而,目前**不建议**使用此方式,这是因为查询循环通过模式和 `scope: [ 'block' ]` 变体进行设置时,所选模式的所有属性将被使用,但 `postType` 和 `inherit` 查询属性除外,这可能导致冲突和变体功能异常。
|
||||
|
||||
为解决此问题,有两种方法。第一种是添加默认的 `innerBlocks`,如下所示:
|
||||
|
||||
```js
|
||||
innerBlocks: [
|
||||
[
|
||||
'core/post-template',
|
||||
{},
|
||||
[ [ 'core/post-title' ], [ 'core/post-excerpt' ] ],
|
||||
],
|
||||
[ 'core/query-pagination' ],
|
||||
[ 'core/query-no-results' ],
|
||||
],
|
||||
```
|
||||
|
||||
通过在变体中包含 `innerBlocks`,您实际上跳过了查询循环区块通过建议模式进行设置的阶段,区块将以这些内部区块作为初始内容插入。
|
||||
|
||||
另一种方法是为您的变体注册特定模式,这些模式将在设置过程中被推荐,并替换区块的流程。
|
||||
|
||||
查询循环区块会判断自身是否有活跃变体,以及该变体是否有特定模式可用。如果有,这些模式将是唯一推荐给用户的模式,不会包含原始查询循环区块的默认模式。否则,如果没有此类模式,将推荐默认模式。
|
||||
|
||||
若要将模式与查询循环变体“关联”,您应在模式的 `blockTypes` 属性中添加以查询循环名称前缀的变体名称(例如 `core/query/$variation_name`)。有关注册模式的更多详情,请[参阅此处](https://developer.wordpress.org/block-editor/reference-guides/block-api/block-patterns/)。
|
||||
|
||||
如果您未在变体中提供 `innerBlocks`,还有一种方法可以在用户于设置阶段选择 `Start blank` 时推荐“关联”变体。这与“关联”模式的处理方式类似,通过检查查询循环是否有活跃变体以及是否有任何关联变体可推荐。
|
||||
|
||||
若要将一个变体与另一个查询循环变体关联,我们需要将 `scope` 属性定义为 `['block']`,并将 `namespace` 属性定义为一个数组。该数组应包含希望关联的变体的名称(`name` 属性)。
|
||||
|
||||
例如,如果我们有一个名称为 `products` 的查询循环变体暴露于插入器中(`scope: ['inserter']`),我们可以通过将其 `namespace` 属性设置为 `['products']` 来关联一个作用域为 `block` 的变体。如果用户在点击 `Start blank` 后选择此变体,`namespace` 属性将被主要的插入器变体覆盖。
|
||||
|
||||
### 3. 让 Gutenberg 识别您的变体
|
||||
|
||||
在实现此变体后,您可能意识到一个小问题:虽然用户在插入时对此无感知,但 Gutenberg 仍会将变体识别为查询循环区块的核心功能,因此在插入后,例如在编辑器的树形视图中,它将显示为查询循环区块。
|
||||
|
||||
我们需要一种方式告诉编辑器,此区块实际上是您的特定变体。这正是 `isActive` 属性的用途:它是一种基于区块属性判断特定变体是否活跃的方法。您可以像这样使用它:
|
||||
|
||||
```js
|
||||
{
|
||||
/** ...变体属性 */
|
||||
isActive: ( { namespace, query } ) => {
|
||||
return (
|
||||
namespace === MY_VARIATION_NAME
|
||||
&& query.postType === 'book'
|
||||
);
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
您可能倾向于仅比较 `postType`,以便当 `postType` 匹配 `book` 时,Gutenberg 会将区块识别为您的变体。然而,这种方式范围过广,因为其他插件可能也希望基于 `book` 文章类型发布变体,或者我们可能不希望用户在编辑器设置中手动将类型设置为 `book` 时每次都识别为变体。
|
||||
|
||||
这就是为什么查询循环区块暴露了一个名为 `namespace` 的特殊属性。它在区块实现中实际上没有任何作用,而是作为一种简单且一致的方式供扩展者识别和限定自己的变体。此外,`isActive` 还可以接受一个字符串数组,用于比较属性。通常,`namespace` 就足够了,您可以这样使用:
|
||||
|
||||
```js
|
||||
{
|
||||
/** ...变体属性 */
|
||||
attributes: {
|
||||
/** ...变体属性 */
|
||||
namespace: 'my-plugin/books-list',
|
||||
},
|
||||
isActive: [ 'namespace' ],
|
||||
}
|
||||
```
|
||||
|
||||
这样,Gutenberg 只有在匹配您的自定义命名空间时才会知道这是您的特定变体!非常方便!
|
||||
278
docs/how-to-guides/block-tutorial/nested-blocks-inner-blocks.md
Normal file
278
docs/how-to-guides/block-tutorial/nested-blocks-inner-blocks.md
Normal file
@@ -0,0 +1,278 @@
|
||||
## 使用 React 钩子
|
||||
|
||||
你可以使用名为 `useInnerBlocksProps` 的 React 钩子来替代 `InnerBlocks` 组件。该钩子能让你更灵活地控制内部区块区域的标记结构。
|
||||
|
||||
`useInnerBlocksProps` 与 `InnerBlocks` 组件同样从 `@wordpress/block-editor` 包中导出,并支持该组件的所有功能。其工作方式类似于 `useBlockProps` 钩子。
|
||||
|
||||
需特别注意:必须在调用 `useInnerBlocksProps` *之前* 调用 `useBlockProps` 钩子,否则 `useBlockProps` 将返回空对象。
|
||||
|
||||
以下是基础的 `useInnerBlocksProps` 钩子用法:
|
||||
|
||||
```js
|
||||
import { registerBlockType } from '@wordpress/blocks';
|
||||
import { useBlockProps, useInnerBlocksProps } from '@wordpress/block-editor';
|
||||
|
||||
registerBlockType( 'gutenberg-examples/example-06', {
|
||||
// ...
|
||||
|
||||
edit: () => {
|
||||
const blockProps = useBlockProps();
|
||||
const innerBlocksProps = useInnerBlocksProps();
|
||||
|
||||
return (
|
||||
<div { ...blockProps }>
|
||||
<div {...innerBlocksProps} />
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
||||
save: () => {
|
||||
const blockProps = useBlockProps.save();
|
||||
const innerBlocksProps = useInnerBlocksProps.save();
|
||||
|
||||
return (
|
||||
<div { ...blockProps }>
|
||||
<div {...innerBlocksProps} />
|
||||
</div>
|
||||
);
|
||||
},
|
||||
} );
|
||||
```
|
||||
|
||||
该钩子还可将 `useBlockProps` 钩子返回的对象传递给 `useInnerBlocksProps` 钩子,从而减少需要创建的元素数量:
|
||||
|
||||
```js
|
||||
import { registerBlockType } from '@wordpress/blocks';
|
||||
import { useBlockProps, useInnerBlocksProps } from '@wordpress/block-editor';
|
||||
|
||||
registerBlockType( 'gutenberg-examples/example-06', {
|
||||
// ...
|
||||
|
||||
edit: () => {
|
||||
const blockProps = useBlockProps();
|
||||
const innerBlocksProps = useInnerBlocksProps( blockProps );
|
||||
|
||||
return (
|
||||
<div {...innerBlocksProps} />
|
||||
);
|
||||
},
|
||||
|
||||
save: () => {
|
||||
const blockProps = useBlockProps.save();
|
||||
const innerBlocksProps = useInnerBlocksProps.save( blockProps );
|
||||
|
||||
return (
|
||||
<div {...innerBlocksProps} />
|
||||
);
|
||||
},
|
||||
} );
|
||||
```
|
||||
|
||||
上述代码将在编辑器中渲染为以下标记结构:
|
||||
|
||||
```html
|
||||
<div>
|
||||
<!-- 内部区块将插入在此处 -->
|
||||
</div>
|
||||
```
|
||||
|
||||
使用钩子方案的另一个优势是:可以利用返回值(仅为一个对象)并通过解构获取其中的 React 子元素。该属性包含实际的子内部区块,因此我们可以将元素放置在与内部区块同一层级:
|
||||
|
||||
```js
|
||||
import { registerBlockType } from '@wordpress/blocks';
|
||||
import { useBlockProps, useInnerBlocksProps } from '@wordpress/block-editor';
|
||||
|
||||
registerBlockType( 'gutenberg-examples/example-06', {
|
||||
// ...
|
||||
|
||||
edit: () => {
|
||||
const blockProps = useBlockProps();
|
||||
const { children, ...innerBlocksProps } = useInnerBlocksProps( blockProps );
|
||||
|
||||
return (
|
||||
<div {...innerBlocksProps}>
|
||||
{ children }
|
||||
<!-- 可在此处插入与子元素同层级的任意HTML -->
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
||||
// ...
|
||||
} );
|
||||
```
|
||||
|
||||
```html
|
||||
<div>
|
||||
<!-- 内部区块将插入在此处 -->
|
||||
<!-- 自定义HTML将渲染于同一层级 -->
|
||||
</div>
|
||||
```
|
||||
|
||||
## 在区块中使用父级、祖先与子级关系
|
||||
|
||||
使用 InnerBlocks 的常见模式是创建一个仅在其父级区块插入时才可用的自定义区块。这使构建者能够在建立区块间关系的同时,限制嵌套区块的可发现性。构建者可使用三种关系类型:`parent`(父级)、`ancestor`(祖先)和 `allowedBlocks`(允许的区块)。它们的区别在于:
|
||||
|
||||
- 若指定 `parent`,则表示嵌套区块仅能作为__父级的直接后代__被使用和插入
|
||||
- 若指定 `ancestor`,则表示嵌套区块仅能作为__父级的后代__被使用和插入
|
||||
- 若指定 `allowedBlocks`,则表示反向关系——即哪些区块可以作为__此区块的直接后代__被使用和插入
|
||||
|
||||
`parent` 与 `ancestor` 的核心区别在于:`parent` 具有更精确的特定性,而 `ancestor` 在嵌套层级中具有更高灵活性。
|
||||
|
||||
### 定义父级区块关系
|
||||
|
||||
典型案例是 Column(列)区块,它被设置了 `parent` 区块配置。这使得 Column 区块仅能作为其父级 Columns(列组)区块的嵌套直接后代使用。否则该区块将不会在区块插入器中显示。参阅 [Column 代码实现](https://github.com/WordPress/gutenberg/tree/HEAD/packages/block-library/src/column)。
|
||||
|
||||
定义直接后代区块时,需使用 `parent` 区块配置来声明父级区块。这能防止嵌套区块在其定义的 InnerBlock 之外显示于插入器中。
|
||||
|
||||
```json
|
||||
{
|
||||
"title": "栏目",
|
||||
"name": "core/column",
|
||||
"parent": [ "core/columns" ],
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### 定义祖先区块关系
|
||||
|
||||
典型案例是 Comment Author Name(评论作者名称)区块,它被设置了 `ancestor` 区块配置。这使得该区块仅能作为其祖先 Comment Template(评论模板)区块的嵌套后代使用。否则该区块将不会在区块插入器中显示。参阅 [Comment Author Name 代码实现](https://github.com/WordPress/gutenberg/tree/HEAD/packages/block-library/src/comment-author-name)。
|
||||
|
||||
通过 `ancestor` 关系,Comment Author Name 区块可以存在于层级树中的任意位置,而__不仅限于__父级 Comment Template 区块的直接子级,同时仍能限制其在区块插入器中的可见性——仅当 Comment Template 区块存在时才显示为可插入选项。
|
||||
|
||||
定义后代区块时,需使用 `ancestor` 区块配置。这能防止嵌套区块在其定义的 InnerBlock 之外显示于插入器中。
|
||||
|
||||
```json
|
||||
{
|
||||
"title": "评论作者名称",
|
||||
"name": "core/comment-author-name",
|
||||
"ancestor": [ "core/comment-template" ],
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### 定义子级区块关系
|
||||
|
||||
典型案例是 Navigation(导航)区块,它被设置了 `allowedBlocks` 区块配置。这使得仅特定类型的区块能作为 Navigation 区块的直接后代使用。参阅 [Navigation 代码实现](https://github.com/WordPress/gutenberg/tree/HEAD/packages/block-library/src/navigation)。
|
||||
|
||||
自定义区块构建者可扩展 `allowedBlocks` 设置。通过挂载 `blocks.registerBlockType` 过滤器,自定义区块可将自身添加到 Navigation 的可用子级列表中。
|
||||
|
||||
定义可能的子代区块集合时,需使用 `allowedBlocks` 区块配置。这会限制插入新子区块时插入器中显示的区块类型。
|
||||
|
||||
```json
|
||||
{
|
||||
"title": "导航",
|
||||
"name": "core/navigation",
|
||||
"allowedBlocks": [ "core/navigation-link", "core/search", "core/social-links", "core/page-list", "core/spacer" ],
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
# 嵌套区块:使用 InnerBlocks
|
||||
|
||||
您可以使用 [InnerBlocks](https://github.com/WordPress/gutenberg/tree/HEAD/packages/block-editor/src/components/inner-blocks/README.md) 组件创建包含其他区块的独立区块。这一技术被广泛应用于分栏区块、社交链接区块等需要容纳其他区块的组件中。
|
||||
|
||||
注意:单个区块只能包含一个 `InnerBlocks` 组件。
|
||||
|
||||
以下是 InnerBlocks 的基本用法:
|
||||
|
||||
```js
|
||||
import { registerBlockType } from '@wordpress/blocks';
|
||||
import { InnerBlocks, useBlockProps } from '@wordpress/block-editor';
|
||||
|
||||
registerBlockType( 'gutenberg-examples/example-06', {
|
||||
// ...
|
||||
|
||||
edit: () => {
|
||||
const blockProps = useBlockProps();
|
||||
|
||||
return (
|
||||
<div { ...blockProps }>
|
||||
<InnerBlocks />
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
||||
save: () => {
|
||||
const blockProps = useBlockProps.save();
|
||||
|
||||
return (
|
||||
<div { ...blockProps }>
|
||||
<InnerBlocks.Content />
|
||||
</div>
|
||||
);
|
||||
},
|
||||
} );
|
||||
```
|
||||
|
||||
## 允许的区块
|
||||
|
||||
通过 `allowedBlocks` 属性,您可以在 `block.json` 的 `allowedBlocks` 字段基础上,进一步限制哪些区块可以作为该区块的直接子级插入。这对于针对每个区块动态确定允许的区块列表非常实用,例如根据区块属性来决定:
|
||||
|
||||
```js
|
||||
const { allowedBlocks } = attributes;
|
||||
//...
|
||||
<InnerBlocks allowedBlocks={ allowedBlocks } />;
|
||||
```
|
||||
|
||||
如果允许的区块列表始终保持不变,建议改用 [`allowedBlocks` 区块设置](#定义子级区块关系)。
|
||||
|
||||
## 排列方向
|
||||
|
||||
默认情况下,`InnerBlocks` 假定其包含的区块以垂直列表形式呈现。在某些应用场景中,可能需要通过为内部区块包装器添加 CSS 弹性布局或网格属性,使内部区块水平排列。当区块采用此类样式时,可通过设置 `orientation` 属性来指示当前正在使用水平布局:
|
||||
|
||||
```js
|
||||
<InnerBlocks orientation="horizontal" />
|
||||
```
|
||||
|
||||
设置此属性不会影响内部区块的布局,但会使子区块中的区块移动器图标水平显示,同时确保拖放功能正常工作。
|
||||
|
||||
## 默认区块
|
||||
|
||||
默认情况下,当点击区块添加器时,`InnerBlocks` 会通过 `allowedBlocks` 显示允许的区块列表。您可以使用 `defaultBlock` 属性来修改点击初始区块添加器时插入的默认区块及其属性。例如:
|
||||
|
||||
```js
|
||||
<InnerBlocks defaultBlock={['core/paragraph', {placeholder: "Lorem ipsum..."}]} directInsert />
|
||||
```
|
||||
|
||||
默认情况下,此功能处于禁用状态,除非将 `directInsert` 属性设置为 `true`。这使您能够指定默认区块是否应该插入的条件。
|
||||
|
||||
## 模板
|
||||
|
||||
使用 template 属性可以定义一组区块,在 InnerBlocks 组件没有现有内容时预填充内容。您可以为这些区块设置属性来定义其用途。以下示例展示了使用 InnerBlocks 组件设置书评模板,并通过占位符值展示区块用途:
|
||||
|
||||
```js
|
||||
const MY_TEMPLATE = [
|
||||
[ 'core/image', {} ],
|
||||
[ 'core/heading', { placeholder: '书名' } ],
|
||||
[ 'core/paragraph', { placeholder: '内容摘要' } ],
|
||||
];
|
||||
|
||||
//...
|
||||
|
||||
edit: () => {
|
||||
return (
|
||||
<InnerBlocks
|
||||
template={ MY_TEMPLATE }
|
||||
templateLock="all"
|
||||
/>
|
||||
);
|
||||
},
|
||||
```
|
||||
|
||||
使用 `templateLock` 属性可以锁定模板。设置为 `all` 时将完全锁定模板,禁止任何修改;设置为 `insert` 时则仅允许重新排序现有区块,禁止插入新区块。更多信息请参阅 [templateLock 文档](https://github.com/WordPress/gutenberg/tree/HEAD/packages/block-editor/src/components/inner-blocks/README.md#templatelock)。
|
||||
|
||||
### 文章模板
|
||||
|
||||
虽然与 `InnerBlocks` 无直接关联,但值得在此说明:您可以按文章类型创建[文章模板](https://developer.wordpress.org/block-editor/developers/block-api/block-templates/),使区块编辑器预加载一组特定区块。
|
||||
|
||||
`InnerBlocks` 模板仅适用于您创建的单个区块组件,而文章的其余部分可以包含用户喜欢的任何区块。使用文章模板则可以将整篇文章锁定为您定义的模板结构。
|
||||
|
||||
```php
|
||||
add_action( 'init', function() {
|
||||
$post_type_object = get_post_type_object( 'post' );
|
||||
$post_type_object->template = array(
|
||||
array( 'core/image' ),
|
||||
array( 'core/heading' )
|
||||
);
|
||||
} );
|
||||
```
|
||||
Reference in New Issue
Block a user