各位掘金的弄潮儿们,大家好!我是你们的老朋友,今天咱们来聊聊WordPress古腾堡编辑器里的一个重量级选手:InnerBlocks
。这玩意儿,说白了,就是让你的块儿可以嵌套其他块儿,就像俄罗斯套娃一样,一层套一层,玩出各种花样。
开场白:为啥我们需要嵌套块?
想象一下,你正在构建一个精美的页面,你想要一个卡片布局,每张卡片里都要有标题、内容和按钮。如果没有InnerBlocks
,你可能需要单独创建每个卡片,然后手动调整它们的样式和布局。这不仅繁琐,而且难以维护。
但有了InnerBlocks
,你可以创建一个“卡片容器”块,然后允许用户在这个容器里添加标题、内容和按钮块。这样,整个卡片就是一个独立的单元,易于管理和复用。这就是嵌套块的魅力!
第一幕:InnerBlocks
的基本概念
InnerBlocks
是古腾堡编辑器提供的一个组件,它允许你定义一个块,该块可以包含其他块。它主要涉及两个方面:
- 父块(Parent Block): 包含其他块的块,也就是“容器”。
- 子块(Child Block): 被父块包含的块,也就是“内容”。
InnerBlocks
组件的核心在于两个:
InnerBlocks.Content
: 用于在父块的编辑界面渲染子块。InnerBlocks.BlockList
: 用于在父块的保存界面渲染子块。
第二幕:代码实战:创建一个简单的嵌套块
咱们先从一个最简单的例子开始,创建一个“容器”块,允许用户在其中添加段落块。
- 注册父块(容器块):
import { registerBlockType } from '@wordpress/blocks';
import { useBlockProps, InnerBlocks } from '@wordpress/block-editor';
registerBlockType('my-plugin/container-block', {
title: '容器块',
icon: 'align-wide',
category: 'common',
edit: ({ className }) => {
return (
<div { ...useBlockProps({
className: className,
}) }>
<h2>容器块</h2>
<InnerBlocks />
</div>
);
},
save: ({ className }) => {
return (
<div { ...useBlockProps.save({
className: className,
}) }>
<h2>容器块</h2>
<InnerBlocks.Content />
</div>
);
},
});
这段代码做了什么?
registerBlockType
:注册一个新的块类型。title
:块的标题,显示在编辑器中。icon
:块的图标。category
:块所属的类别。edit
:编辑界面,使用InnerBlocks
组件渲染子块。save
:保存界面,使用InnerBlocks.Content
组件渲染子块。useBlockProps
:获取默认的块属性, 并传递 className 属性
- 解释关键部分:
- 在
edit
函数中,<InnerBlocks />
组件负责渲染子块的编辑界面。当用户在这个容器块中添加新的块时,它们会出现在这个区域。 - 在
save
函数中,<InnerBlocks.Content />
组件负责渲染子块的保存界面。它会将子块的内容保存到文章内容中。
第三幕:控制允许的子块类型
仅仅允许添加任何类型的块是不够的,有时候我们需要限制用户只能添加特定类型的块。比如,在我们的卡片容器中,我们可能只想允许添加标题、内容和按钮块。
我们可以使用allowedBlocks
属性来控制允许的子块类型。
import { registerBlockType } from '@wordpress/blocks';
import { useBlockProps, InnerBlocks } from '@wordpress/block-editor';
registerBlockType('my-plugin/card-container', {
title: '卡片容器',
icon: 'align-wide',
category: 'common',
attributes: {
backgroundColor: {
type: 'string',
default: '#f0f0f0',
},
},
edit: ({ className, attributes, setAttributes }) => {
const { backgroundColor } = attributes;
const onChangeBackgroundColor = (newColor) => {
setAttributes({ backgroundColor: newColor });
};
const allowedBlocks = ['core/heading', 'core/paragraph', 'core/button'];
const blockProps = useBlockProps({
className: className,
style: { backgroundColor: backgroundColor },
});
return (
<div { ...blockProps }>
<h2>卡片容器</h2>
<div className="card-content">
<InnerBlocks allowedBlocks={allowedBlocks} />
</div>
</div>
);
},
save: ({ className, attributes }) => {
const { backgroundColor } = attributes;
const blockProps = useBlockProps.save({
className: className,
style: { backgroundColor: backgroundColor },
});
return (
<div { ...blockProps }>
<div className="card-content">
<InnerBlocks.Content />
</div>
</div>
);
},
});
在这个例子中,allowedBlocks
属性被设置为一个包含'core/heading'
、'core/paragraph'
和'core/button'
的数组。这意味着用户只能在这个卡片容器中添加标题、段落和按钮块。
第四幕:模板(Templates):预定义子块结构
有时候,我们希望在用户添加容器块时,自动包含一些预定义的子块。例如,我们希望卡片容器默认包含一个标题和一个段落。
我们可以使用template
属性来实现这个功能。
import { registerBlockType } from '@wordpress/blocks';
import { useBlockProps, InnerBlocks } from '@wordpress/block-editor';
registerBlockType('my-plugin/card-container-with-template', {
title: '卡片容器(带模板)',
icon: 'align-wide',
category: 'common',
edit: ({ className }) => {
const template = [
['core/heading', { placeholder: '输入标题' }],
['core/paragraph', { placeholder: '输入内容' }],
];
return (
<div { ...useBlockProps({
className: className,
}) }>
<h2>卡片容器(带模板)</h2>
<InnerBlocks template={template} />
</div>
);
},
save: ({ className }) => {
return (
<div { ...useBlockProps.save({
className: className,
}) }>
<h2>卡片容器(带模板)</h2>
<InnerBlocks.Content />
</div>
);
},
});
在这个例子中,template
属性被设置为一个包含两个元素的数组。每个元素都是一个数组,第一个元素是块的名称,第二个元素是块的属性。
['core/heading', { placeholder: '输入标题' }]
:创建一个标题块,并设置其placeholder
属性为“输入标题”。['core/paragraph', { placeholder: '输入内容' }]
:创建一个段落块,并设置其placeholder
属性为“输入内容”。
当用户添加这个卡片容器块时,它会自动包含一个标题和一个段落,并且标题和段落的placeholder
属性会被设置为指定的值。
第五幕:锁住你的块(Block Locking):控制用户操作
有时候,我们希望限制用户对子块的操作,比如禁止删除或移动它们。我们可以使用templateLock
属性来实现这个功能。
templateLock
属性有三个可能的值:
false
:允许用户自由操作子块。'all'
:禁止用户删除、移动或添加子块。'insert'
:只允许用户添加子块,禁止删除和移动。
import { registerBlockType } from '@wordpress/blocks';
import { useBlockProps, InnerBlocks } from '@wordpress/block-editor';
registerBlockType('my-plugin/card-container-with-lock', {
title: '卡片容器(带锁定)',
icon: 'align-wide',
category: 'common',
edit: ({ className }) => {
const template = [
['core/heading', { placeholder: '输入标题' }],
['core/paragraph', { placeholder: '输入内容' }],
];
return (
<div { ...useBlockProps({
className: className,
}) }>
<h2>卡片容器(带锁定)</h2>
<InnerBlocks template={template} templateLock="all" />
</div>
);
},
save: ({ className }) => {
return (
<div { ...useBlockProps.save({
className: className,
}) }>
<h2>卡片容器(带锁定)</h2>
<InnerBlocks.Content />
</div>
);
},
});
在这个例子中,templateLock
属性被设置为'all'
。这意味着用户无法删除、移动或添加子块。
第六幕:深入挖掘:useInnerBlocksProps
Hook
useInnerBlocksProps
是一个 React Hook,它提供了一种更灵活的方式来配置 InnerBlocks
组件的行为。它返回一个包含 props 的对象,你可以将这些 props 传递给你的自定义组件,从而实现对子块渲染的更精细控制。
import { registerBlockType } from '@wordpress/blocks';
import { useBlockProps, InnerBlocks, useInnerBlocksProps } from '@wordpress/block-editor';
registerBlockType('my-plugin/custom-container', {
title: '自定义容器',
icon: 'align-wide',
category: 'common',
edit: ({ className }) => {
const innerBlocksProps = useInnerBlocksProps({
className: 'my-custom-inner-blocks',
});
return (
<div { ...useBlockProps({
className: className,
}) }>
<h2>自定义容器</h2>
<div { ...innerBlocksProps } />
</div>
);
},
save: ({ className }) => {
const innerBlocksProps = useInnerBlocksProps.save({});
return (
<div { ...useBlockProps.save({
className: className,
}) }>
<h2>自定义容器</h2>
<div { ...innerBlocksProps } />
</div>
);
},
});
在这个例子中,我们使用了 useInnerBlocksProps
hook 来获取 props,然后将这些 props 传递给一个自定义的 div
元素。这允许我们为子块的渲染区域添加自定义的 class name (my-custom-inner-blocks
),从而可以更灵活地控制样式。
useInnerBlocksProps
的常见用法:
- 自定义容器样式: 通过添加自定义的 class name 或 style,可以改变子块渲染区域的外观。
- 处理子块的事件: 可以监听子块的事件,例如添加、删除或移动事件,并执行相应的操作。
- 与其他组件集成: 可以将
useInnerBlocksProps
与其他 React 组件集成,从而实现更复杂的布局和交互。
第七幕:高级技巧:动态渲染子块
在某些情况下,我们可能需要根据父块的属性来动态渲染子块。例如,我们可能希望创建一个“标签页”块,用户可以添加任意数量的标签页,每个标签页都包含一个标题和一个内容区域。
要实现这个功能,我们需要使用JavaScript来动态生成子块的配置。
import { registerBlockType } from '@wordpress/blocks';
import { useBlockProps, InnerBlocks, InspectorControls } from '@wordpress/block-editor';
import { PanelBody, RangeControl } from '@wordpress/components';
registerBlockType('my-plugin/tab-container', {
title: '标签页容器',
icon: 'align-wide',
category: 'common',
attributes: {
numberOfTabs: {
type: 'number',
default: 2,
},
},
edit: ({ attributes, setAttributes }) => {
const { numberOfTabs } = attributes;
const onChangeNumberOfTabs = (newNumberOfTabs) => {
setAttributes({ numberOfTabs: newNumberOfTabs });
};
const template = Array(numberOfTabs)
.fill()
.map((_, index) => ['my-plugin/tab-item', { title: `标签页 ${index + 1}` }]);
return (
<>
<InspectorControls>
<PanelBody title="标签页设置">
<RangeControl
label="标签页数量"
value={numberOfTabs}
onChange={onChangeNumberOfTabs}
min={1}
max={5}
/>
</PanelBody>
</InspectorControls>
<div { ...useBlockProps() }>
<h2>标签页容器</h2>
<InnerBlocks template={template} allowedBlocks={['my-plugin/tab-item']} templateLock="insert" />
</div>
</>
);
},
save: ({ attributes }) => {
return (
<div { ...useBlockProps.save() }>
<InnerBlocks.Content />
</div>
);
},
});
registerBlockType('my-plugin/tab-item', {
title: '标签页',
icon: 'align-wide',
category: 'common',
parent: ['my-plugin/tab-container'],
attributes: {
title: {
type: 'string',
default: '标签页',
},
},
edit: ({ attributes, setAttributes }) => {
const { title } = attributes;
const onChangeTitle = (newTitle) => {
setAttributes({ title: newTitle });
};
return (
<div { ...useBlockProps() }>
<h3>{title}</h3>
<InnerBlocks />
</div>
);
},
save: ({ attributes }) => {
const { title } = attributes;
return (
<div { ...useBlockProps.save() }>
<h3>{title}</h3>
<InnerBlocks.Content />
</div>
);
},
});
在这个例子中:
- 父块(
my-plugin/tab-container
): 允许用户控制标签页的数量。 - 子块(
my-plugin/tab-item
): 表示一个单独的标签页,包含一个标题和一个内容区域。 - 动态模板: 父块的
edit
函数使用numberOfTabs
属性来动态生成template
数组。 - InspectorControls: 允许用户在侧边栏调整标签页的数量。
parent
属性: 子块的parent
属性限制它只能被添加到my-plugin/tab-container
块中。
第八幕:常见问题与注意事项
- 性能问题: 过度使用嵌套块可能会导致性能问题,特别是当嵌套层级很深时。尽量避免不必要的嵌套,并优化你的块代码。
- 用户体验: 复杂的嵌套结构可能会让用户感到困惑。确保你的块设计简单易懂,并提供清晰的视觉提示。
- 兼容性: 确保你的块与不同的主题和插件兼容。测试你的块在各种环境下是否正常工作。
clientId
的重要性: 每个块都有一个唯一的clientId
,它用于在编辑器中标识块。在处理嵌套块时,clientId
非常重要,因为它可以帮助你找到特定的子块。- 更新子块属性: 如果你需要在父块中更新子块的属性,可以使用
wp.data
API 来 dispatch 一个 action。
第九幕:总结与展望
InnerBlocks
是古腾堡编辑器中一个非常强大的工具,它允许你创建复杂的、可复用的块结构。通过灵活运用allowedBlocks
、template
和templateLock
属性,你可以构建各种各样的嵌套块,满足不同的需求。
当然,InnerBlocks
还有很多高级用法等待你去探索。例如,你可以使用自定义的渲染函数来控制子块的显示方式,或者使用服务端渲染来优化性能。
希望今天的分享对你有所帮助。记住,编程的乐趣在于不断学习和实践。祝你玩得开心!
最后的彩蛋:一些实用技巧
技巧 | 说明 |
---|---|
使用 block.json 文件 |
将块的注册信息放在 block.json 文件中,可以提高代码的可维护性。 |
使用 useEntityProp hook |
这个 hook 可以让你轻松地访问和更新文章的元数据,例如文章的标题、内容和自定义字段。 |
使用 withSelect 和 withDispatch |
这两个高阶组件可以让你将 WordPress 数据存储中的数据和 action 映射到你的块组件中。 |
善用控制台调试 | 在开发过程中,使用浏览器的控制台可以帮助你调试代码,查看错误信息和变量的值。 |
好了,今天的分享就到这里。希望大家有所收获,下次再见!