gutenbergdocs/docs/how-to-guides/block-tutorial/nested-blocks-inner-blocks.md

278 lines
10 KiB
Markdown
Raw Permalink Normal View History

2025-10-21 17:33:45 +00:00
## 使用 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' )
);
} );
```