278 lines
10 KiB
Markdown
278 lines
10 KiB
Markdown
|
|
## 使用 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' )
|
|||
|
|
);
|
|||
|
|
} );
|
|||
|
|
```
|