Blocks 字段
Blocks 字段功能极其强大,它基于你定义的字段存储一个对象数组,其中数组中的每一项都是一个具有自己独特模式的"块"。
Blocks 是创建灵活内容模型的绝佳方式,可用于构建多种内容类型,包括:
- 布局构建工具:让编辑者能够设计高度可定制的页面或文章布局。块可以包含诸如
Quote
(引用)、CallToAction
(行动号召)、Slider
(轮播)、Content
(内容)或Gallery
(画廊)等配置。 - 表单构建工具:可用的块配置可能包括
Text
(文本)、Select
(选择)或Checkbox
(复选框)。 - 虚拟活动议程的"时间段":每个时间段可以是
Break
(休息)、Presentation
(演讲)或BreakoutSession
(分组讨论)。


要添加 Blocks 字段,请在你的字段配置中将 type
设置为 blocks
:
import type { Field } from 'payload'
export const MyBlocksField: Field = {
// ...
// highlight-start
type: 'blocks',
blocks: [
// ...
],
// highlight-end
}
配置选项
选项 | 描述 |
---|---|
name * | 作为属性名存储在数据库中并从数据库中检索时使用。了解更多 |
label | 在管理面板中用作标题的文本,或为每种语言提供键的对象。如果未定义,则根据名称自动生成。 |
blocks * | 可供此字段使用的块配置数组。 |
validate | 提供自定义验证函数,该函数将在管理面板和后端执行。了解更多 |
minRows | 当存在值时,验证期间允许的最小项目数。 |
maxRows | 当存在值时,验证期间允许的最大项目数。 |
saveToJWT | 如果此字段是顶级字段并嵌套在支持身份验证的配置中,则将其数据包含在用户 JWT 中。 |
hooks | 提供字段钩子来控制此字段的逻辑。更多详情。 |
access | 提供字段访问控制,以指定用户可以对此字段的数据执行的操作。更多详情。 |
hidden | 完全限制此字段在所有 API 中的可见性。仍会保存到数据库,但不会出现在任何 API 响应或管理面板中。 |
defaultValue | 提供用于此字段默认值的块数据数组。了解更多 |
localized | 为此字段启用本地化。需要在基础配置中启用本地化。如果启用,将为此字段中的所有数据保留单独的本地化集合,因此无需将每个嵌套字段指定为 localized 。 |
unique | 强制集合中的每个条目对此字段具有唯一值。 |
labels | 自定义管理仪表板中显示的块行标签。 |
admin | 特定于管理面板的配置。更多详情。 |
custom | 用于添加自定义数据(例如插件)的扩展点。 |
typescriptSchema | 通过提供 JSON 模式覆盖字段类型生成。 |
virtual | 提供 true 以禁用数据库中的字段,或提供字符串路径以将字段与关系链接。参见虚拟字段。 |
* 星号表示该属性为必填项。
管理选项
要自定义 Admin Panel 中 Blocks Field 的外观和行为,可以使用 admin
选项:
import type { Field } from 'payload'
export const MyBlocksField: Field = {
// ...
admin: {
// highlight-line
// ...
},
}
Blocks Field 继承了基础 Field Admin Config 的所有默认管理选项,并新增了以下额外选项:
选项 | 描述 |
---|---|
group | 用于在 Blocks Drawer 中分组该 Block 的文本或本地化对象。 |
initCollapsed | 设置初始折叠状态 |
isSortable | 将此值设为 false 可禁用排序功能 |
disableBlockName | 将此值设为 true 可隐藏 blockName 字段 |
在 Lexical 编辑器中自定义块的渲染方式
如果你在 Lexical 编辑器 中使用这个块,还可以通过指定自定义组件来定制块在 Lexical 编辑器中的渲染方式。
admin.components.Label
- 传入一个自定义 React 组件,用于定制该块的标签渲染方式admin.components.Block
- 传入一个组件,可以完全覆盖 Lexical 中块的渲染方式,使用你自己的组件
如果你想在富文本编辑器中为编辑人员呈现精心设计且美观的块"预览",这将非常有用。
例如,如果你有一个 gallery
块,你可能希望在 Lexical 块中直接渲染图片画廊。通过 admin.components.Block
属性,你可以轻松实现这一点!
提示: 如果自定义了块在 Lexical 中的渲染方式,你可以导入实用组件来轻松编辑/删除你的块,这样你就不需要自己构建这些功能了。
要为你的自定义块导入这些实用组件,可以使用以下导入语句:
import {
// 编辑块按钮(根据你的使用场景选择对应的按钮)
// 点击后会打开一个抽屉,显示块的字段
// 让编辑人员可以编辑这些字段
InlineBlockEditButton,
BlockEditButton,
// 从 Lexical 中移除该块的按钮
//(根据你的使用场景选择对应的按钮)
InlineBlockRemoveButton,
BlockRemoveButton,
// 内联块应渲染的标签
InlineBlockLabel,
// 内联块的默认"容器"渲染组件
// 如果你想复用这个容器
InlineBlockContainer,
// 常规块的默认"可折叠"UI组件
// 如果你想复用这个UI
BlockCollapsible,
} from '@payloadcms/richtext-lexical/client'
区块配置
区块需要作为独立的配置进行定义。
提示: 最佳实践是将每个区块配置定义在单独的文件中,然后根据需要导入到你的 Blocks 字段中。这样每个区块配置可以轻松地在多个字段间共享。例如,在"布局构建器"的例子中,你可能希望在 Post 集合和 Page 集合中都使用几个相同的区块。将它们抽象到单独的文件中可以轻松实现复用。
Option | Description |
---|---|
slug * | 该区块类型的标识符。会作为 blockType 属性保存在每个区块中。 |
fields * | 存储在该区块中的字段数组。 |
labels | 自定义在管理后台显示的区块标签。如果未定义,会根据 slug 自动生成。 |
imageURL | 提供自定义的缩略图图片,帮助编辑者在管理界面中识别该区块。 |
imageAltText | 自定义该区块缩略图的 alt 文本。 |
interfaceName | 创建顶层的可复用 Typescript 接口 和 GraphQL 类型。 |
graphQL.singularName | 用于 GraphQL 模式名称的文本。如果未定义,会根据 slug 自动生成。注意:此选项将被弃用,建议使用 interfaceName 。 |
dbName | 使用 SQL 数据库适配器(Postgres)时,该区块类型的自定义表名。如果未定义,会根据 slug 自动生成。 |
custom | 用于添加自定义数据的扩展点(例如插件使用) |
每个区块自动生成的数据
除了你在每个区块上定义的字段数据外,Payload 还会在每个区块上存储两个额外的属性:
blockType
blockType
会被保存为所选区块的 slug。
blockName
Admin Panel 为每个区块提供一个 blockName
字段,允许编辑者可选地为区块添加标签,以提高可编辑性和可读性。可以通过 admin.disableBlockName
来隐藏这个字段。
示例
collections/ExampleCollection.js
import { Block, CollectionConfig } from 'payload'
const QuoteBlock: Block = {
slug: 'Quote', // 必填
imageURL: 'https://google.com/path/to/image.jpg',
imageAltText: '展示该区块外观的缩略图',
interfaceName: 'QuoteBlock', // 可选
fields: [
// 必填
{
name: 'quoteHeader',
type: 'text',
required: true,
},
{
name: 'quoteText',
type: 'text',
},
],
}
export const ExampleCollection: CollectionConfig = {
slug: 'example-collection',
fields: [
{
name: 'layout', // 必填
type: 'blocks', // 必填
minRows: 1,
maxRows: 20,
blocks: [
// 必填
QuoteBlock,
],
},
],
}
自定义组件
字段
服务端组件
import type React from 'react'
import { BlocksField } from '@payloadcms/ui'
import type { BlocksFieldServerComponent } from 'payload'
export const CustomBlocksFieldServer: BlocksFieldServerComponent = ({
clientField,
path,
schemaPath,
permissions,
}) => {
return (
<BlocksField
field={clientField}
path={path}
schemaPath={schemaPath}
permissions={permissions}
/>
)
}
客户端组件
'use client'
import React from 'react'
import { BlocksField } from '@payloadcms/ui'
import type { BlocksFieldClientComponent } from 'payload'
export const CustomBlocksFieldClient: BlocksFieldClientComponent = (props) => {
return <BlocksField {...props} />
}
标签
服务端组件
import React from 'react'
import { FieldLabel } from '@payloadcms/ui'
import type { BlocksFieldLabelServerComponent } from 'payload'
export const CustomBlocksFieldLabelServer: BlocksFieldLabelServerComponent = ({
clientField,
path,
}) => {
return (
<FieldLabel
label={clientField?.label || clientField?.name}
path={path}
required={clientField?.required}
/>
)
}
客户端组件
'use client'
import React from 'react'
import { FieldLabel } from '@payloadcms/ui'
import type { BlocksFieldLabelClientComponent } from 'payload'
export const CustomBlocksFieldLabelClient: BlocksFieldLabelClientComponent = ({
label,
path,
required,
}) => {
return (
<FieldLabel
label={field?.label || field?.name}
path={path}
required={field?.required}
/>
)
}
行标签
'use client'
import { useRowLabel } from '@payloadcms/ui'
export const BlockRowLabel = () => {
const { data, rowNumber } = useRowLabel<{ title?: string }>()
const customLabel = `${data.type} ${String(rowNumber).padStart(2, '0')} `
return <div>自定义标签: {customLabel}</div>
}
区块引用
如果在多个地方使用多个区块,你的 Payload 配置可能会变得庞大,这可能导致向客户端发送更多数据,并在服务器上需要更多处理。不过,你可以通过一次性定义每个区块并在使用时仅引用其 slug 来优化性能,而不是传递整个区块配置。
要实现这一点,首先在 Payload 配置的 blocks
数组中定义区块。然后,在 Blocks Field 中,将区块 slug 传递给 blockReferences
数组 - 出于兼容性原因,blocks
数组需要保持为空。
import { buildConfig } from 'payload'
import { lexicalEditor, BlocksFeature } from '@payloadcms/richtext-lexical'
// Payload 配置
const config = buildConfig({
// 一次性定义区块
blocks: [
{
slug: 'TextBlock',
fields: [
{
name: 'text',
type: 'text',
},
],
},
],
collections: [
{
slug: 'collection1',
fields: [
{
name: 'content',
type: 'blocks',
// 通过 slug 引用区块
blockReferences: ['TextBlock'],
blocks: [], // 出于兼容性原因,必须为空
},
],
},
{
slug: 'collection2',
fields: [
{
name: 'editor',
type: 'richText',
editor: lexicalEditor({
features: [
BlocksFeature({
// 相同的引用可以在任何地方重复使用,甚至在 lexical 编辑器中也不会影响性能
blocks: ['TextBlock'],
}),
],
}),
},
],
},
],
})
提醒:
在 blockReferences
数组中引用的区块被视为与 collection/global 配置隔离。这有以下影响:
- 区块配置不能在 collection 配置中被修改或扩展。它在所有引用处都将保持一致。
- 在
blockReferences
中引用的区块的访问控制只会运行一次 - collection 中的数据将不会在区块的访问控制中可用。
TypeScript
在构建你自己的 Block 配置时,你可能希望将它们存储在单独的文件中,同时保留类型定义。为此,你可以导入并使用 Payload 的 Block
类型:
import type { Block } from 'payload'