您可以使用 InnerBlocks 组件创建一个嵌套其他块的单个块。这在 Columns 块、Social Links 块或任何您希望包含其他块的块中都会用到。
注意:单个块只能包含一个 InnerBlocks 组件。
以下是 InnerBlocks 的基本用法。
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 字段外)哪些块可以作为此块的直接子代插入。这对于动态确定每个块的允许块列表非常有用。例如,由块属性确定:
const { allowedBlocks } = attributes;
//...
<InnerBlocks allowedBlocks={ allowedBlocks } />;
如果允许的块列表始终相同,请优先使用 allowedBlocks 块设置。
方向
默认情况下,InnerBlocks 期望其块以垂直列表形式显示。一个有效的用例是使内部块水平显示,例如通过为内部块包装器添加 CSS flex 或 grid 属性。当块以这种方式样式化时,可以将 orientation 属性设置为指示正在使用水平布局:
<InnerBlocks orientation="horizontal" />
指定此属性不会影响内部块的布局,但会导致子块中的块移动器图标水平显示,并确保拖放正常工作。
默认块
默认情况下,当点击块附加器时,InnerBlocks 会通过 allowedBlocks 打开允许的块列表。您可以使用 defaultBlock 属性修改初始块附加器点击时插入的默认块及其属性。例如:
<InnerBlocks
defaultBlock={
{ name: 'core/paragraph', attributes: { content: 'Lorem ipsum...' } }
}
directInsert
/>
将 directInsert 设置为 true 可强制附加器立即插入 defaultBlock,绕过插入器下拉菜单(包括该块类型的任何已注册的插入器变体)。将其留空(或 false)以让附加器打开插入器,以便用户可以选择变体。
注意:当 allowedBlocks 解析为没有变体的单个块类型时,这将是多余的,因为附加器已经直接插入。
模板
使用 template 属性定义一组块,在 InnerBlocks 组件没有现有内容时预填充该组件。您可以设置块的属性来定义其用途。下面的示例显示了使用 InnerBlocks 组件的书籍评论模板,并设置占位符值以显示块用法。
const MY_TEMPLATE = [
[ 'core/image', {} ],
[ 'core/heading', { placeholder: 'Book Title' } ],
[ 'core/paragraph', { placeholder: 'Summary' } ],
];
//...
edit: () => {
return (
<InnerBlocks
template={ MY_TEMPLATE }
templateLock="all"
/>
);
},
使用 templateLock 属性锁定模板。使用 all 会完全锁定模板,因此无法进行任何更改。使用 insert 可防止插入额外的块,但可以对现有块进行重新排序。有关更多信息,请参见 templateLock 文档。
帖子模板
与 InnerBlocks 无关,但在此值得一提,您可以按帖子类型创建 帖子模板,该模板会预加载一组块的块编辑器。
InnerBlocks 模板是您创建的单个块中的组件,帖子的其余部分可以包含用户喜欢的任何块。使用帖子模板可以将整个帖子锁定为您定义的模板。
add_action( 'init', function() {
$post_type_object = get_post_type_object( 'post' );
$post_type_object->template = array(
array( 'core/image' ),
array( 'core/heading' )
);
} );
在块中使用父级、祖先和子代关系
使用 InnerBlocks 的常见模式是创建一个自定义块,该块仅在插入其父块时可用。这允许构建者建立块之间的关系,同时限制嵌套块的发现性。构建者可以使用三种关系:parent、ancestor 和 allowedBlocks。区别如下:
- 如果您分配了
parent,则表示嵌套块只能作为父级的直接子代使用和插入。 - 如果您分配了
ancestor,则表示嵌套块只能作为父级的后代使用和插入。 - 如果您分配了
allowedBlocks,则表示相反方向的关系,即哪些块可以作为此块的直接子代使用和插入。
parent 和 ancestor 之间的关键区别在于 parent 具有更细的特定性,而 ancestor 在其嵌套层次结构中具有更大的灵活性。
定义父块关系
Column 块就是一个例子,它被分配了 parent 块设置。这使得 Column 块仅作为其父 Columns 块中的嵌套直接子代可用。否则,Column 块将不会作为块插入器中的选项可用。参见 Column 代码参考。
在定义直接子代块时,使用 parent 块设置来定义哪个块是父级。这可以防止嵌套块在非 InnerBlock 定义的插入器中显示。
{
"title": "Column",
"name": "core/column",
"parent": [ "core/columns" ],
// ...
}
定义祖先块关系
Comment Author Name 块就是一个例子,它被分配了 ancestor 块设置。这使得 Comment Author Name 块仅作为其祖传 Comment Template 块中的嵌套后代可用。否则,Comment Author Name 块将不会作为块插入器中的选项可用。参见 Comment Author Name 代码参考。
ancestor 关系允许 Comment Author Name 块位于层次树中的任何位置,而不仅仅是父 Comment Template 块的直接子代,同时仍然将其在块插入器中的可用性限制为仅在 Comment Template 块可用时作为插入选项可见。
在定义后代块时,使用 ancestor 块设置。这可以防止嵌套块在非 InnerBlock 定义的插入器中显示。
{
"title": "Comment Author Name",
"name": "core/comment-author-name",
"ancestor": [ "core/comment-template" ],
// ...
}
定义子块关系
Navigation 块就是一个例子,它被分配了 allowedBlocks 块设置。这使得只有某些子集类型的块可以作为 Navigation 块的直接子代可用。参见 Navigation 代码参考。
allowedBlocks 设置可以由自定义块的构建者扩展。自定义块可以挂钩到 blocks.registerBlockType 过滤器,并将自身添加到 Navigation 的可用子代中。
在定义一组可能的后代块时,使用 allowedBlocks 块设置。这限制了插入新子块时在插入器中显示的块。
{
"title": "Navigation",
"name": "core/navigation",
"allowedBlocks": [ "core/navigation-link", "core/search", "core/social-links", "core/page-list", "core/spacer" ],
// ...
}
使用 React 钩子
你可以使用名为 useInnerBlocksProps 的 React Hook 来替代 InnerBlocks 组件。该 Hook 允许你更精细地控制内联区块区域的标记结构。
useInnerBlocksProps 与 InnerBlocks 组件本身一样,都是从 @wordpress/block-editor 包中导出的,并且支持组件的所有功能。它的工作方式也与 useBlockProps Hook 类似。
需要注意的是,必须在使用 useInnerBlocksProps 之前 调用 useBlockProps Hook,否则 useBlockProps 将返回一个空对象。
以下是 useInnerBlocksProps Hook 的基本用法。
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>
);
},
} );
该 Hook 还可以将 useBlockProps Hook 返回的对象传递给 useInnerBlocksProps Hook。这样可以减少我们需要创建的元素数量。
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} />
);
},
} );
上述代码将在编辑器中渲染为以下标记结构:
<div>
<!-- Inner Blocks get inserted here -->
</div>
使用 Hook 方法的另一个好处是,利用返回的值(仅是一个对象)并进行解构以获取 React 子元素。该属性包含实际的子内联区块,因此我们可以将元素放置在与我们的内联区块同一层级的位置。
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 }
{/* Insert any arbitrary html here at the same level as the children */}
</div>
);
},
// ...
} );
<div>
<!-- Inner Blocks get inserted here -->
<!-- The custom html gets rendered on the same level -->
</div>