10 KiB
使用 React 钩子
你可以使用名为 useInnerBlocksProps 的 React 钩子来替代 InnerBlocks 组件。该钩子能让你更灵活地控制内部区块区域的标记结构。
useInnerBlocksProps 与 InnerBlocks 组件同样从 @wordpress/block-editor 包中导出,并支持该组件的所有功能。其工作方式类似于 useBlockProps 钩子。
需特别注意:必须在调用 useInnerBlocksProps 之前 调用 useBlockProps 钩子,否则 useBlockProps 将返回空对象。
以下是基础的 useInnerBlocksProps 钩子用法:
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 钩子,从而减少需要创建的元素数量:
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>
<!-- 内部区块将插入在此处 -->
</div>
使用钩子方案的另一个优势是:可以利用返回值(仅为一个对象)并通过解构获取其中的 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 }
<!-- 可在此处插入与子元素同层级的任意HTML -->
</div>
);
},
// ...
} );
<div>
<!-- 内部区块将插入在此处 -->
<!-- 自定义HTML将渲染于同一层级 -->
</div>
在区块中使用父级、祖先与子级关系
使用 InnerBlocks 的常见模式是创建一个仅在其父级区块插入时才可用的自定义区块。这使构建者能够在建立区块间关系的同时,限制嵌套区块的可发现性。构建者可使用三种关系类型:parent(父级)、ancestor(祖先)和 allowedBlocks(允许的区块)。它们的区别在于:
- 若指定
parent,则表示嵌套区块仅能作为__父级的直接后代__被使用和插入 - 若指定
ancestor,则表示嵌套区块仅能作为__父级的后代__被使用和插入 - 若指定
allowedBlocks,则表示反向关系——即哪些区块可以作为__此区块的直接后代__被使用和插入
parent 与 ancestor 的核心区别在于:parent 具有更精确的特定性,而 ancestor 在嵌套层级中具有更高灵活性。
定义父级区块关系
典型案例是 Column(列)区块,它被设置了 parent 区块配置。这使得 Column 区块仅能作为其父级 Columns(列组)区块的嵌套直接后代使用。否则该区块将不会在区块插入器中显示。参阅 Column 代码实现。
定义直接后代区块时,需使用 parent 区块配置来声明父级区块。这能防止嵌套区块在其定义的 InnerBlock 之外显示于插入器中。
{
"title": "栏目",
"name": "core/column",
"parent": [ "core/columns" ],
// ...
}
定义祖先区块关系
典型案例是 Comment Author Name(评论作者名称)区块,它被设置了 ancestor 区块配置。这使得该区块仅能作为其祖先 Comment Template(评论模板)区块的嵌套后代使用。否则该区块将不会在区块插入器中显示。参阅 Comment Author Name 代码实现。
通过 ancestor 关系,Comment Author Name 区块可以存在于层级树中的任意位置,而__不仅限于__父级 Comment Template 区块的直接子级,同时仍能限制其在区块插入器中的可见性——仅当 Comment Template 区块存在时才显示为可插入选项。
定义后代区块时,需使用 ancestor 区块配置。这能防止嵌套区块在其定义的 InnerBlock 之外显示于插入器中。
{
"title": "评论作者名称",
"name": "core/comment-author-name",
"ancestor": [ "core/comment-template" ],
// ...
}
定义子级区块关系
典型案例是 Navigation(导航)区块,它被设置了 allowedBlocks 区块配置。这使得仅特定类型的区块能作为 Navigation 区块的直接后代使用。参阅 Navigation 代码实现。
自定义区块构建者可扩展 allowedBlocks 设置。通过挂载 blocks.registerBlockType 过滤器,自定义区块可将自身添加到 Navigation 的可用子级列表中。
定义可能的子代区块集合时,需使用 allowedBlocks 区块配置。这会限制插入新子区块时插入器中显示的区块类型。
{
"title": "导航",
"name": "core/navigation",
"allowedBlocks": [ "core/navigation-link", "core/search", "core/social-links", "core/page-list", "core/spacer" ],
// ...
}
嵌套区块:使用 InnerBlocks
您可以使用 InnerBlocks 组件创建包含其他区块的独立区块。这一技术被广泛应用于分栏区块、社交链接区块等需要容纳其他区块的组件中。
注意:单个区块只能包含一个 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 弹性布局或网格属性,使内部区块水平排列。当区块采用此类样式时,可通过设置 orientation 属性来指示当前正在使用水平布局:
<InnerBlocks orientation="horizontal" />
设置此属性不会影响内部区块的布局,但会使子区块中的区块移动器图标水平显示,同时确保拖放功能正常工作。
默认区块
默认情况下,当点击区块添加器时,InnerBlocks 会通过 allowedBlocks 显示允许的区块列表。您可以使用 defaultBlock 属性来修改点击初始区块添加器时插入的默认区块及其属性。例如:
<InnerBlocks defaultBlock={['core/paragraph', {placeholder: "Lorem ipsum..."}]} directInsert />
默认情况下,此功能处于禁用状态,除非将 directInsert 属性设置为 true。这使您能够指定默认区块是否应该插入的条件。
模板
使用 template 属性可以定义一组区块,在 InnerBlocks 组件没有现有内容时预填充内容。您可以为这些区块设置属性来定义其用途。以下示例展示了使用 InnerBlocks 组件设置书评模板,并通过占位符值展示区块用途:
const MY_TEMPLATE = [
[ 'core/image', {} ],
[ 'core/heading', { placeholder: '书名' } ],
[ 'core/paragraph', { placeholder: '内容摘要' } ],
];
//...
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' )
);
} );