Lexical 迁移指南
从 Slate 迁移
虽然 Slate 和 Lexical 都将编辑器状态保存为 JSON,但它们的 JSON 结构有所不同。
通过迁移脚本迁移(推荐)
只需导入我们提供的 migrateSlateToLexical
函数,传入 payload
对象并运行它。根据集合数量,这可能需要一些时间。
重要提示:这将覆盖所有 Slate 数据。我们建议先执行以下操作:
- 备份整个数据库。如果出现问题且没有备份,你将无法获得任何支持。
- 将所有 richText 字段转换为 lexical 编辑器。此脚本只会转换带有旧 Slate 数据的 lexical richText 字段。
- 首先添加 SlateToLexicalFeature(如下所示),并通过加载 Admin Panel 进行测试,查看迁移器是否按预期工作。对于某些字段,可能需要先构建一些自定义转换器来转换自定义 Slate 节点。SlateToLexicalFeature 是存储这些转换器的地方。只有添加了此功能的字段才会被迁移。
- 如果一切正常,在所有初始化
SlateToLexicalFeature
的地方添加disableHooks: true
属性。例如:SlateToLexicalFeature({ disableHooks: true })
。完成后,即可运行迁移脚本。
import { migrateSlateToLexical } from '@payloadcms/richtext-lexical/migrate'
await migrateSlateToLexical({ payload })
通过 SlateToLexicalFeature 进行迁移
一种处理方式是让你的 lexical 编辑器能够读取 slate JSON 数据。
只需将 SlateToLexicalFeature
添加到你的编辑器中:
import type { CollectionConfig } from 'payload'
import { SlateToLexicalFeature } from '@payloadcms/richtext-lexical/migrate'
import { lexicalEditor } from '@payloadcms/richtext-lexical'
const Pages: CollectionConfig = {
slug: 'pages',
fields: [
{
name: 'nameOfYourRichTextField',
type: 'richText',
editor: lexicalEditor({
features: ({ defaultFeatures }) => [
...defaultFeatures,
SlateToLexicalFeature({}),
],
}),
},
],
}
这样就完成了!现在,每当这个 lexical 编辑器初始化时,它都会实时将 slate 数据转换为 lexical 格式。如果数据已经是 lexical 格式,则会直接传递。
这是目前从 Slate 迁移到 Lexical 最简单的方法,但需要注意以下几点:
- 初始化 lexical 编辑器时会有性能损耗
- 编辑器在输出 JSON 时仍会保留 Slate 数据,因为实时转换器仅针对 Admin Panel 运行
简单的解决方案是:编辑 richText 字段并保存文档!这会用 lexical 数据覆盖 slate 数据,下次加载文档时将使用 lexical 数据。这既解决了性能问题,也解决了该特定文档的输出问题。然而,这是一个缓慢渐进式的迁移过程,因此你需要同时支持两种 API 格式。特别是对于大量文档,我们建议运行上述提到的迁移脚本。
转换自定义 Slate 节点
如果你有自定义的 Slate 节点,需要为它们创建自定义转换器。以下是 Upload 转换器的示例:
import type { SerializedUploadNode } from '../uploadNode'
import type { SlateNodeConverter } from '@payloadcms/richtext-lexical'
export const SlateUploadConverter: SlateNodeConverter = {
converter({ slateNode }) {
return {
fields: {
...slateNode.fields,
},
format: '',
relationTo: slateNode.relationTo,
type: 'upload',
value: {
id: slateNode.value?.id || '',
},
version: 1,
} as const as SerializedUploadNode
},
nodeTypes: ['upload'],
}
原理很简单:你接收一个 Slate 节点作为输入,然后返回 lexical 节点。nodeTypes
数组用于确定该转换器能处理哪些 Slate 节点。
在使用迁移脚本时,你可以将自定义转换器添加到 convertSlateToLexical
属性的 converters
中,如上例所示。
当使用 SlateToLexicalFeature
时,你可以将自定义转换器添加到 SlateToLexicalFeature
属性的 converters
中:
import type { CollectionConfig } from 'payload'
import { lexicalEditor } from '@payloadcms/richtext-lexical'
import {
SlateToLexicalFeature,
defaultSlateConverters,
} from '@payloadcms/richtext-lexical'
import { YourCustomConverter } from '../converters/YourCustomConverter'
const Pages: CollectionConfig = {
slug: 'pages',
fields: [
{
name: 'nameOfYourRichTextField',
type: 'richText',
editor: lexicalEditor({
features: ({ defaultFeatures }) => [
...defaultFeatures,
SlateToLexicalFeature({
converters: [...defaultSlateConverters, YourCustomConverter],
}),
],
}),
},
],
}
从 payload-plugin-lexical 迁移
从 payload-plugin-lexical 迁移的过程与从 Slate 迁移类似。
你可以使用 LexicalPluginToLexicalFeature
替代 SlateToLexicalFeature
,并使用 convertLexicalPluginToLexical
替代 convertSlateToLexical
。
将旧版本的 lexical 数据迁移到新版本
每个 lexical 节点都有一个保存在数据库中的 version
属性。每当我们对节点数据进行破坏性变更时,都会递增版本号。这样,我们可以检测到旧版本数据,并在你打开编辑器时自动将旧数据转换为新格式。
问题是,这种迁移只会在你打开编辑器、修改 richText 字段(从而调用字段的 setValue
函数)并保存文档时发生。在你对所有文档执行此操作之前,部分文档仍将保留旧数据。
为解决这个问题,我们导出了一个 upgradeLexicalData
函数,它会遍历 Payload 应用中的每个文档,如果文档包含 lexical 编辑器,则重新保存它。这样,数据会自动转换为新格式,并且这种自动转换会应用到应用中的所有文档。
重要提示:请备份整个数据库。如果出现任何问题且你没有备份,你将无法获得任何支持。
import { upgradeLexicalData } from '@payloadcms/richtext-lexical'
await upgradeLexicalData({ payload })