HTML 转换
富文本转 HTML
将基于 Lexical 的富文本转换为 HTML 主要有两种方法:
- 按需生成 HTML(推荐):在需要的地方将 JSON 转换为 HTML,按需处理。
- 在 Collection 中生成 HTML:创建一个新字段,自动将保存的 JSON 内容转换为 HTML。这种方法不推荐,因为它会增加 Payload API 的负担,并且可能与实时预览功能不兼容。
按需转换
要按需将 JSON 转换为 HTML,可以使用 @payloadcms/richtext-lexical/html
中的 convertLexicalToHTML
函数。以下是在前端 React 组件中使用该函数的示例:
'use client'
import type { SerializedEditorState } from '@payloadcms/richtext-lexical/lexical'
import { convertLexicalToHTML } from '@payloadcms/richtext-lexical/html'
import React from 'react'
export const MyComponent = ({ data }: { data: SerializedEditorState }) => {
const html = convertLexicalToHTML({ data })
return <div dangerouslySetInnerHTML={{ __html: html }} />
}
动态填充(高级用法)
默认情况下,convertLexicalToHTML
需要完全填充的数据(例如上传文件、链接等)。如果你需要动态获取并填充这些节点,可以使用异步变体 convertLexicalToHTMLAsync
,它来自 @payloadcms/richtext-lexical/html-async
。你必须提供一个 populate
函数:
'use client'
import type { SerializedEditorState } from '@payloadcms/richtext-lexical/lexical'
import { getRestPopulateFn } from '@payloadcms/richtext-lexical/client'
import { convertLexicalToHTMLAsync } from '@payloadcms/richtext-lexical/html-async'
import React, { useEffect, useState } from 'react'
export const MyComponent = ({ data }: { data: SerializedEditorState }) => {
const [html, setHTML] = useState<null | string>(null)
useEffect(() => {
async function convert() {
const html = await convertLexicalToHTMLAsync({
data,
populate: getRestPopulateFn({
apiURL: `http://localhost:3000/api`,
}),
})
setHTML(html)
}
void convert()
}, [data])
return html && <div dangerouslySetInnerHTML={{ __html: html }} />
}
使用 REST populate 函数会为每个节点发送单独的请求。如果你需要填充大量节点,这可能会很慢。为了在服务器上获得更好的性能,你可以使用 getPayloadPopulateFn
函数:
import type { SerializedEditorState } from '@payloadcms/richtext-lexical/lexical'
import { getPayloadPopulateFn } from '@payloadcms/richtext-lexical'
import { convertLexicalToHTMLAsync } from '@payloadcms/richtext-lexical/html-async'
import { getPayload } from 'payload'
import React from 'react'
import config from '../../config.js'
export const MyRSCComponent = async ({
data,
}: {
data: SerializedEditorState
}) => {
const payload = await getPayload({
config,
})
const html = await convertLexicalToHTMLAsync({
data,
populate: await getPayloadPopulateFn({
currentDepth: 0,
depth: 1,
payload,
}),
})
return html && <div dangerouslySetInnerHTML={{ __html: html }} />
}
HTML 字段
lexicalHTMLField()
辅助函数会将 JSON 转换为 HTML,并通过 afterRead
钩子在每次读取时更新保存到字段中。通常不建议使用此方法,原因有二:
- 它会在另一列中创建重复内容的另一种格式
- 在客户端实时预览中,会导致预览不再"实时"
除非有充分理由,否则建议使用按需 HTML 转换器或JSX 转换器。
import type { HTMLConvertersFunction } from '@payloadcms/richtext-lexical/html'
import type { MyTextBlock } from '@/payload-types.js'
import type { CollectionConfig } from 'payload'
import {
BlocksFeature,
type DefaultNodeTypes,
lexicalEditor,
lexicalHTMLField,
type SerializedBlockNode,
} from '@payloadcms/richtext-lexical'
const Pages: CollectionConfig = {
slug: 'pages',
fields: [
{
name: 'nameOfYourRichTextField',
type: 'richText',
editor: lexicalEditor(),
},
lexicalHTMLField({
htmlFieldName: 'nameOfYourRichTextField_html',
lexicalFieldName: 'nameOfYourRichTextField',
}),
{
name: 'customRichText',
type: 'richText',
editor: lexicalEditor({
features: ({ defaultFeatures }) => [
...defaultFeatures,
BlocksFeature({
blocks: [
{
interfaceName: 'MyTextBlock',
slug: 'myTextBlock',
fields: [
{
name: 'text',
type: 'text',
},
],
},
],
}),
],
}),
},
lexicalHTMLField({
htmlFieldName: 'customRichText_html',
lexicalFieldName: 'customRichText',
// 可以传入额外的转换器或覆盖默认转换器
converters: (({ defaultConverters }) => ({
...defaultConverters,
blocks: {
myTextBlock: ({ node, providedCSSString }) =>
`<div style="background-color: red;${providedCSSString}">${node.fields.text}</div>`,
},
})) as HTMLConvertersFunction<
DefaultNodeTypes | SerializedBlockNode<MyTextBlock>
>,
}),
],
}
将 Blocks 转换为 HTML
如果你的富文本包含 Lexical blocks,你需要提供将其转换为 HTML 的方法。例如:
'use client'
import type { MyInlineBlock, MyTextBlock } from '@/payload-types'
import type {
DefaultNodeTypes,
SerializedBlockNode,
SerializedInlineBlockNode,
} from '@payloadcms/richtext-lexical'
import type { SerializedEditorState } from '@payloadcms/richtext-lexical/lexical'
import {
convertLexicalToHTML,
type HTMLConvertersFunction,
} from '@payloadcms/richtext-lexical/html'
import React from 'react'
type NodeTypes =
| DefaultNodeTypes
| SerializedBlockNode<MyTextBlock>
| SerializedInlineBlockNode<MyInlineBlock>
const htmlConverters: HTMLConvertersFunction<NodeTypes> = ({
defaultConverters,
}) => ({
...defaultConverters,
blocks: {
// 每个键应该匹配你的 block 的 slug
myTextBlock: ({ node, providedCSSString }) =>
`<div style="background-color: red;${providedCSSString}">${node.fields.text}</div>`,
},
inlineBlocks: {
// 每个键应该匹配你的 inline block 的 slug
myInlineBlock: ({ node, providedStyleTag }) =>
`<span${providedStyleTag}>${node.fields.text}</span$>`,
},
})
export const MyComponent = ({ data }: { data: SerializedEditorState }) => {
const html = convertLexicalToHTML({
converters: htmlConverters,
data,
})
return <div dangerouslySetInnerHTML={{ __html: html }} />
}
HTML 转 Richtext
如果你需要将原始 HTML 转换为 Lexical 编辑器状态,可以使用 @payloadcms/richtext-lexical
中的 convertHTMLToLexical
方法,配合 editorConfigFactory 获取编辑器配置:
import {
convertHTMLToLexical,
editorConfigFactory,
} from '@payloadcms/richtext-lexical'
// 确保已安装 jsdom 和 @types/jsdom
import { JSDOM } from 'jsdom'
const html = convertHTMLToLexical({
editorConfig: await editorConfigFactory.default({
config, // 你的 Payload 配置
}),
html: '<p>text</p>',
JSDOM, // 传入 JSDOM 导入;为了保持包体积小,它没有被捆绑
})