title: "嵌套区块:使用 InnerBlocks" post_status: publish comment_status: open taxonomy: category: - gutenberg-docs post_tag: - Block Tutorial - How To Guides - Repos


嵌套区块:使用 InnerBlocks

您可以使用 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 之前处于禁用状态。这允许您指定何时应插入或不应插入默认区块的条件。

模板

使用 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 的常见模式是创建一个自定义区块,该区块仅在其父级区块被插入时才可用。这允许构建者建立区块之间的关系,同时限制嵌套区块的可发现性。构建者可以使用三种关系:parentancestorallowedBlocks。它们的区别如下:

parentancestor 的关键区别在于,parent 具有更精细的特定性,而 ancestor 在其嵌套层次结构中具有更大的灵活性。

定义父级区块关系

以 Column 区块为例,它被赋予了 parent 区块设置。这使得 Column 区块仅能作为其父级 Columns 区块的嵌套直接子级使用。否则,Column 区块将不会在区块插入器中作为可选选项出现。请参阅 Column 代码作为参考

当定义一个直接子级区块时,使用 parent 区块设置来指定哪个区块是父级。这可以防止嵌套区块在其定义的 InnerBlock 之外显示在插入器中。

{
    "title": "Column",
    "name": "core/column",
    "parent": [ "core/columns" ],
    // ...
}

定义祖先块关系

以评论作者姓名块为例,该块被分配了 ancestor 块设置。这使得评论作者姓名块仅能作为其祖先评论模板块的嵌套后代使用。否则,评论作者姓名块将不会作为选项出现在区块插入器中。请参阅评论作者姓名代码作为参考

ancestor 关系允许评论作者姓名块位于层级树中的任何位置,而不仅仅是父评论模板块的直接子块,同时仍限制其在区块插入器中的可用性,即仅当评论模板块可用时才显示为可插入选项。

定义后代块时,请使用 ancestor 块设置。这可以防止嵌套块在其定义的 InnerBlock 之外显示在插入器中。

{
    "title": "Comment Author Name",
    "name": "core/comment-author-name",
    "ancestor": [ "core/comment-template" ],
    // ...
}

定义子区块关系

以导航区块为例,它被分配了 allowedBlocks 区块设置。这使得只有特定的区块类型子集可以作为导航区块的直接子级使用。请参考导航代码

自定义区块的构建者可以扩展 allowedBlocks 设置。自定义区块可以挂载到 blocks.registerBlockType 过滤器,并将自身添加到导航区块的可用子级列表中。

当定义一组可能的子级区块时,请使用 allowedBlocks 区块设置。这会限制在插入新的子区块时,插入器中显示的区块类型。

{
    "title": "Navigation",
    "name": "core/navigation",
    "allowedBlocks": [ "core/navigation-link", "core/search", "core/social-links", "core/page-list", "core/spacer" ],
    // ...
}

Using a React hook

You can use a react hook called useInnerBlocksProps instead of the InnerBlocks component. This hook allows you to take more control over the markup of inner blocks areas.

The useInnerBlocksProps is exported from the @wordpress/block-editor package same as the InnerBlocks component itself and supports everything the component does. It also works like the useBlockProps hook.

It is important to note that useBlockProps hook must be called before useInnerBlocksProps, otherwise useBlockProps will return empty object.

Here is the basic useInnerBlocksProps hook usage.

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>
        );
    },
} );

This hook can also pass objects returned from the useBlockProps hook to the useInnerBlocksProps hook. This reduces the number of elements we need to create.

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} />
        );
    },
} );

The above code will render to the following markup in the editor:

<div>
    <!-- Inner Blocks get inserted here -->
</div>

Another benefit to using the hook approach is using the returned value, which is just an object, and deconstruct to get the react children from the object. This property contains the actual child inner blocks thus we can place elements on the same level as our inner blocks.

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>