本指南涵盖将Plate编辑器内容转换为HTML(serializeHtml
)以及将HTML解析回Plate格式(editor.api.html.deserialize
)的操作。
套件使用
安装
启用HTML序列化的最快方式是使用BaseEditorKit
,它包含预配置的基础插件,支持大多数常见元素和标记的HTML转换。
添加套件
import { createSlateEditor, serializeHtml } from 'platejs';
import { BaseEditorKit } from '@/components/editor/editor-base-kit';
const editor = createSlateEditor({
plugins: BaseEditorKit,
value: [
{ type: 'h1', children: [{ text: 'Hello World' }] },
{ type: 'p', children: [{ text: '此内容将被序列化为HTML。' }] },
],
});
// 序列化为HTML
const html = await serializeHtml(editor);
示例
查看完整的服务端HTML生成示例:
Plate转HTML
将Plate编辑器内容(Plate节点)转换为HTML字符串。这通常在服务端完成。
关键服务端限制
在服务端环境(Node.js, RSC)中使用serializeHtml
或其他Plate工具时,不得从任何platejs*
包的/react
子路径导入。始终使用基础导入(例如使用@platejs/basic-nodes
而非@platejs/basic-nodes/react
)。
这意味着服务端编辑器实例应使用platejs
中的createSlateEditor
,而非platejs/react
中的usePlateEditor
或createPlateEditor
。
基础用法
提供服务端编辑器实例并在编辑器创建时配置Plate组件。
import { createSlateEditor, serializeHtml } from 'platejs'; // 基础导入
// 导入基础插件(不从/react路径导入)
import { BaseHeadingPlugin } from '@platejs/basic-nodes';
// 导入用于渲染的静态组件
import { ParagraphElementStatic } from '@/components/ui/paragraph-node-static';
import { HeadingElementStatic } from '@/components/ui/heading-node-static';
// 对于带样式的静态输出,可以使用像EditorStatic这样的包装器
import { EditorStatic } from '@/components/ui/editor-static';
// 将插件键映射到其静态渲染组件
const components = {
p: ParagraphElementStatic, // 'p'是段落的默认键
h1: HeadingElementStatic,
// ... 为所有元素和标记添加映射
};
// 创建带组件的服务端编辑器实例
const editor = createSlateEditor({
plugins: [
BaseHeadingPlugin, // 标题基础插件
// ... 添加与内容相关的所有其他基础插件
],
components,
});
async function getMyHtml() {
// 示例:在服务端编辑器上设置内容
editor.children = [
{ type: 'h1', children: [{text: '我的标题'}] },
{ type: 'p', children: [{text: '我的内容。'}] }
];
const html = await serializeHtml(editor, {
// 可选:使用像EditorStatic这样的自定义包装器进行样式设置
// editorComponent: EditorStatic,
// props: { variant: 'none', className: 'p-4 m-4 border' },
});
return html;
}
序列化HTML的样式设置
serializeHtml
仅返回编辑器内容本身的HTML。如果使用带样式的组件(如EditorStatic
或具有特定类的自定义静态组件),必须确保最终显示HTML的上下文中包含必要的CSS。
这通常意味着将序列化的HTML包装在包含样式表的完整HTML文档中:
// ...(来自generate-html.ts的先前设置)
async function getFullHtmlDocument() {
const editorHtmlContent = await getMyHtml(); // 来自之前的示例
const fullHtml = `<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="/path/to/your-global-styles.css" />
<link rel="stylesheet" href="/path/to/tailwind-or-component-styles.css" />
<title>序列化内容</title>
</head>
<body>
<div class="my-document-wrapper prose dark:prose-invert">
${editorHtmlContent}
</div>
</body>
</html>`;
return fullHtml;
}
仅静态输出
序列化过程将Plate节点转换为静态HTML。交互功能(React事件处理程序、客户端钩子)或依赖浏览器API的组件在序列化输出中将无法工作。
使用静态组件
对于服务端序列化,必须使用组件的静态版本(无仅客户端代码,无React钩子如useEffect
或useState
)。
参考静态渲染指南获取为Plate元素和标记创建服务端安全静态组件的详细说明。
import React from 'react';
import type { SlateElementProps } from 'platejs';
// 示例静态段落组件
export function ParagraphElementStatic(props: SlateElementProps) {
return (
<SlateElement {...props} className={cn('m-0 px-0 py-1')}>
{props.children}
</SlateElement>
);
}
HTML转Plate
HTML反序列化器允许将HTML内容(字符串或DOM元素)转换回Plate格式。这支持往返转换,在存在对应插件规则的情况下保留结构、格式和属性。
基础用法
在客户端Plate编辑器上下文中使用editor.api.html.deserialize
。
import { PlateEditor, usePlateEditor } from 'platejs/react'; // 客户端专用的React导入
// 导入表示HTML内容所需的所有Plate插件
import { HeadingPlugin } from '@platejs/basic-nodes/react';
// ... 以及粗体、斜体、表格、列表等的插件
function MyHtmlImporter({ htmlString }: { htmlString: string }) {
const editor = usePlateEditor({
plugins: [
HeadingPlugin, // 用于<h1>, <h2>等
// ... 包含与预期解析的HTML对应的所有插件
],
});
const handleImport = () => {
const slateValue = editor.api.html.deserialize(htmlString);
editor.tf.setValue(slateValue);
};
// ... 渲染编辑器及触发handleImport的按钮 ...
return <button onClick={handleImport}>导入HTML</button>;
}
客户端操作
使用editor.api.html.deserialize
的HTML反序列化通常是客户端操作,因为它与配置了React组件和插件的实时Plate编辑器实例交互。
插件反序列化规则概览
每个Plate插件可以定义规则,说明在反序列化期间如何解释特定的HTML标签、样式和属性。下表是常见HTML结构及通常负责它们的Plate插件的摘要。
HTML元素/样式 | Plate插件(典型) | 备注 |
---|---|---|
<strong> , <b> , font-weight: 600,700,bold | BoldPlugin | 转换为bold: true 标记。 |
<em> , <i> , font-style: italic | ItalicPlugin | 转换为italic: true 标记。 |
<u> , text-decoration: underline | UnderlinePlugin | 转换为underline: true 标记。 |
<s> , <del> , <strike> , text-decoration: line-through | StrikethroughPlugin | 转换为strikethrough: true 标记。 |
<sub> , vertical-align: sub | SubscriptPlugin | 转换为subscript: true 标记。 |
<sup> , vertical-align: super | SuperscriptPlugin | 转换为superscript: true 标记。 |
<code> (不在<pre> 中), font-family: Consolas | CodePlugin | 转换为code: true 标记(内联代码)。 |
<kbd> | KbdPlugin | 转换为kbd: true 标记。 |
<p> | ParagraphPlugin | 转换为段落元素。 |
<h1> - <h6> | HeadingPlugin | 转换为对应的标题元素(h1 - h6 )。 |
<ul> | ListPlugin (经典) | 转换为无序列表(ul 类型)。项目变为li 。 |
<ol> | ListPlugin (经典) | 转换为有序列表(ol 类型)。项目变为li 。 |
<li> (在<ul> 或<ol> 中) | ListPlugin (经典) | 转换为列表项(li 类型),带有lic (列表项内容)子项。 |
<li> (带aria-level 缩进) | ListPlugin | 转换为带indent 和listStyleType 属性的段落。 |
<blockquote> | BlockquotePlugin | 转换为blockquote元素。 |
<pre> (通常内部有<code> ) | CodeBlockPlugin | 转换为code_block 元素。内容分割为code_line 。 |
<hr> | HorizontalRulePlugin | 转换为水平线元素。 |
<a> | LinkPlugin | 转换为链接元素(a 类型)带url 属性。 |
<img> | ImagePlugin | 转换为图像元素(img 类型)带url 属性。 |
<iframe> | MediaEmbedPlugin | 转换为媒体嵌入元素,尝试解析URL。 |
<table> | TablePlugin | 转换为table 元素。 |
<tr> | TablePlugin | 转换为tr (表格行)元素。 |
<td> | TablePlugin | 转换为td (表格单元格)元素。 |
<th> | TablePlugin | 转换为th (表格标题单元格)元素。 |
style="background-color: ..." | FontColorPlugin | 转换为backgroundColor 标记。(插件名称可能看似相反) |
style="color: ..." | FontColorPlugin | 转换为color 标记。 |
style="font-family: ..." | FontFamilyPlugin | 转换为fontFamily 标记。 |
style="font-size: ..." | FontSizePlugin | 转换为fontSize 标记。 |
style="font-weight: ..." (非粗体值) | FontWeightPlugin | 为非标准粗体值转换为fontWeight 标记。 |
<mark> | HighlightPlugin | 转换为highlight: true 标记。 |
style="text-align: ..." | TextAlignPlugin | 在块元素上设置align 属性。 |
style="line-height: ..." | LineHeightPlugin | 在块元素上设置lineHeight 属性。 |
插件配置
确切的Plate类型(例如ParagraphPlugin.key
与'p'
)取决于插件的配置方式。表格显示典型关联。确保编辑器包含对应的Plate插件以使这些规则生效。
插件中的反序列化属性
插件可以在其parsers.html.deserializer
配置中定义如何处理HTML反序列化:
parse
: 函数({ editor, element, getOptions, ... }) => Partial<SlateNode>
,接收HTML元素并返回部分Plate节点。这是主要转换逻辑所在。query
: 可选函数({ element, getOptions }) => boolean
,确定是否应考虑当前HTML元素的反序列化规则。rules
: 规则对象数组,每个定义匹配HTML元素的条件:validNodeName
: 字符串或字符串数组,用于匹配HTML标签名(例如'P'
,['STRONG', 'B']
)。validAttribute
: 对象或对象数组,指定必需的属性名和/或值(例如{ align: ['left', 'center'] }
)。validClassName
: 字符串或字符串数组,用于匹配CSS类名。validStyle
: 对象或对象数组,指定必需的CSS样式属性和/或值(例如{ fontWeight: ['600', '700', 'bold'] }
)。
isElement
: 布尔值,true
表示插件将HTML元素反序列化为Plate Element节点。isLeaf
: 布尔值,true
表示插件将HTML元素或样式反序列化为Text节点上的Plate Leaf(标记)。attributeNames
: HTML属性名数组,其值应保留在结果Plate节点的node.attributes
属性上。withoutChildren
: 布尔值,如果true
,HTML元素的子节点不会被convertHtmlAttributes
处理。
自定义反序列化行为
可以扩展插件以修改其HTML解析逻辑。这对于支持非标准HTML属性或结构非常有用。
import { CodeBlockPlugin } from '@platejs/code-block/react';
import { CodeLinePlugin } from '@platejs/code-block'; // 如果需要基础
const MyCustomCodeBlockPlugin = CodeBlockPlugin.configure({
parsers: {
html: {
deserializer: {
// 继承大多数规则和属性,然后覆盖或添加
...CodeBlockPlugin.parsers.html.deserializer,
parse: ({ element, editor }) => { // 可能需要editor用于getType
const language = element.getAttribute('data-custom-lang') || element.className.match(/language-(?<lang>[^\s]+)/)?.groups?.lang;
const textContent = element.textContent || '';
const lines = textContent.split('\n');
return {
type: CodeBlockPlugin.key, // 或editor.getType(CodeBlockPlugin.key)
lang: language,
code: textContent, // 示例:存储完整代码字符串
children: lines.map((line) => ({
type: editor.getType(CodeLinePlugin.key),
children: [{ text: line }],
})),
};
},
rules: [
// 如果需要,继承现有规则
...(CodeBlockPlugin.parsers.html.deserializer.rules || []),
// 添加基于自定义属性的新规则
{ validAttribute: { 'data-custom-lang': true } },
],
},
},
},
});
// 然后在编辑器配置中使用MyCustomCodeBlockPlugin。
此示例自定义CodeBlockPlugin
以查找data-custom-lang
属性或language-*
类来确定代码语言。
高级反序列化示例(ListPlugin
)
ListPlugin
展示了一个更复杂的反序列化场景,它将HTML列表结构(<li>
元素)转换为Plate中的缩进段落,使用aria-level
确定缩进。
以下是其反序列化逻辑的概念性展示:
// 来自ListPlugin的简化概念
export const ListPluginConfig = {
// ... 其他配置 ...
parsers: {
html: {
deserializer: {
isElement: true,
// query: ({ element }) => hasListAncestor(element), // 示例条件
parse: ({ editor, element }) => ({
type: 'p', // 将<li>转换为<p>
indent: Number(element.getAttribute('aria-level') || '1'),
listStyleType: element.style.listStyleType || undefined,
// 子节点在此节点创建后由Plate的默认反序列化器处理
}),
rules: [
{ validNodeName: 'LI' }, // 仅适用于<li>元素
],
},
},
},
};
这展示了插件如何完全将HTML结构重新解释为不同的Plate表示。
API 参考
serializeHtml(editor, options)
将 editor.children
(或提供的 value
)中的 Plate 节点转换为 HTML 字符串。此函数通常在服务端使用。
editorComponent optional React.ComponentType<P>
用于在静态渲染期间包装整个编辑器内容的 React 组件。默认为
PlateStatic
。 该组件接收editor
、value
以及此处传递的任何props
。props optional Partial<P>
传递给
editorComponent
的 props。P
默认为PlateStaticProps
。value optional Descendant[]
要序列化的 Plate 节点。如果未提供,将使用
editor.children
。preserveClassNames optional string[] | null
如果
stripClassNames
为 true,则保留的类名前缀数组。如果stripClassNames
为 true,null
将保留所有类名。默认值:['slate-', 'line-clamp']
。stripClassNames optional boolean
如果为
true
,则从输出 HTML 中删除所有类名,但保留preserveClassNames
中列出的前缀的类名。默认值:true
。stripDataAttributes optional boolean
如果为
true
,则从输出 HTML 中删除所有data-*
属性。默认值:true
。
api.html.deserialize(options)
将 HTML 字符串或 HTMLElement
解析为 Plate Value
(Descendant
节点数组)。这通常在客户端与完全配置的 Plate 编辑器一起使用。
下一步
- 探索静态渲染指南以创建服务端安全的组件。
- 查看各个插件文档以了解特定的 HTML 序列化/反序列化功能和默认规则。
- 查看服务端 HTML 生成示例。