Serializing HTML
HTML <-> Slate
'use client';
import React from 'react';
import { Plate } from '@udecode/plate-common/react';
import { editorPlugins } from '@/components/editor/plugins/editor-plugins';
import { useCreateEditor } from '@/components/editor/use-create-editor';
import { Editor, EditorContainer } from '@/components/plate-ui/editor';
import { DEMO_VALUES } from './values/demo-values';
export default function Demo({ id }: { id: string }) {
const editor = useCreateEditor({
plugins: [...editorPlugins],
value: DEMO_VALUES[id],
});
return (
<Plate editor={editor}>
<EditorContainer variant="demo">
<Editor />
</EditorContainer>
</Plate>
);
}
注意: 回环转换尚未支持:HTML 序列化器不会保留 Slate 值中的所有信息,当从 HTML 转换回 Slate 时。
Slate -> HTML
Usage
// ...
import { createSlateEditor, serializeHtml } from '@udecode/plate-common';
import { EditorStatic } from '@/components/plate-ui/editor-static';
// 创建一个编辑器并配置所有需要的插件
const editor = createSlateEditor({
// ... your plugins ...
});
// 提供将 Slate 节点映射到 HTML 元素的组件
const components = {
// [ParagraphPlugin.key]: ParagraphElementStatic,
// [HeadingPlugin.key]: HeadingElementStatic,
// ...
};
// 你还可以传递一个自定义编辑器组件和 props
// 例如,EditorStatic 是 PlateStatic 的样式版本。
const html = await serializeHtml(editor, {
components,
editorComponent: EditorStatic, // defaults to PlateStatic if not provided
props: { variant: 'none', className: 'p-2' },
});如果你使用一个自定义组件,比如 EditorStatic,你必须确保所有需要的样式和类都包含在你的最终 HTML 文件中。由于 serialize 只返回内部编辑器 HTML,你可能需要将其包装在一个完整的 HTML 文档中,包含任何外部 CSS、脚本或 <style> 标签。
例如:
// 序列化内容后:
const html = await serializeHtml(editor, {
components,
editorComponent: EditorStatic,
props: { variant: 'none' },
});
// 将 HTML 包装在一个完整的 HTML 文档中
const fullHtml = `<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="/path/to/tailwind.css" />
<!-- other head elements -->
</head>
<body>
${html}
</body>
</html>`;使用静态组件
当序列化 Slate 内容到 HTML 时,你必须使用静态版本的组件。你的交互式、“客户端”组件通常依赖于浏览器 API 或客户端 React 特性,这些特性在服务器端渲染(SSR)期间是不可用的。
这意味着什么?
- 交互组件: 使用
use client、事件处理程序(onClick)或仅限浏览器的 API(如window或document)的组件无法在服务器上运行。 - 静态组件: 相反,对于序列化,你必须提供这些组件的静态版本。静态组件应该:
- 不包含任何事件处理程序,如
onClick。 - 不依赖于浏览器 API 或客户端钩子。
- 简单地渲染为纯 HTML。
- 不包含任何事件处理程序,如
示例:
如果你有一个使用 use client 和事件处理程序的客户端组件,如 ImageElement,你需要创建一个静态版本 ImageElementStatic,它只返回一个带有所有属性的纯 <img> 标签,没有交互性。
对于每个插件键,提供其对应的静态组件在 components 对象中传递给 serializeHtml:
// 使用静态组件,这些组件不依赖于 'use client',
// 它们只是返回纯 HTML。
import { createSlateEditor, serializeHtml } from '@udecode/plate-common';
// 导入静态版本的组件
import { ParagraphElementStatic } from '@/components/plate-ui/paragraph-element-static';
import { HeadingElementStatic } from '@/components/plate-ui/heading-element-static';
import { ImageElementStatic } from '@/components/plate-ui/image-element-static';
// ... 每个插件的静态版本 ...
const editor = createSlateEditor({ /* ...plugins... */ });
const components = {
[BaseParagraphPlugin.key]: ParagraphElementStatic,
[BaseHeadingPlugin.key]: HeadingElementStatic,
[BaseImagePlugin.key]: ImageElementStatic,
// ... 其他静态组件 ...
};
const html = await serializeHtml(editor, { components });PlateStatic vs. Plate
在不需要交互式环境的情况下渲染编辑器输出时,你有两个主要选择:PlateStatic 和 Plate。两者都旨在生成静态的、不可编辑的内容视图,但它们在目的和性能特征上有所不同。
PlateStatic
- 目的: 一个更轻量级的、服务器兼容的编辑器内容静态渲染器。
- 性能: 在只读模式下通常比
Plate性能更好。 - 使用场景: 当你需要内容的静态预览时(例如,在服务器端渲染或静态构建中)最为理想。如果你想要内容的快速、静态表示,
PlateStatic应该是默认选择。
Plate (只读模式)
- 目的: 使用
readOnly={true}的标准Plate编辑器组件。虽然这样可以防止编辑,但它仍然作为依赖浏览器 API 的客户端组件运行。 - 使用场景: 适用于在浏览器中渲染且需要交互环境(例如评论)但恰好是不可编辑的情况。它不适用于服务器端 HTML 序列化或需要静态输出的场景,因为它仍然运行客户端代码。
总结:
- 如果你需要高性能、静态视图或 HTML 序列化: 使用
PlateStatic。 - 如果你需要交互式环境且可以在浏览器中运行,但只想要只读模式: 使用带有
readOnly={true}的Plate。
API
serializeHtml
将 Slate 节点转换为 HTML 字符串。
Parameters
控制 HTML 序列化过程的选项。
Returns
表示 Slate 内容的 HTML 字符串。
HTML -> Slate
使用方法
The editor.api.html.deserialize function allows you to convert HTML content into Slate value:
import { createPlateEditor } from '@udecode/plate-common/react';
const editor = createPlateEditor({
plugins: [
// all plugins that you want to deserialize
]
})
editor.children = editor.api.html.deserialize('<p>Hello, world!</p>')插件反序列化规则
以下是支持 HTML 反序列化的插件列表,以及它们对应的 HTML 元素和样式:
文本格式化
- BoldPlugin:
<strong>、<b>或style="font-weight: 600|700|bold" - ItalicPlugin:
<em>、<i>或style="font-style: italic" - UnderlinePlugin:
<u>或style="text-decoration: underline" - StrikethroughPlugin:
<s>、<del>、<strike>或style="text-decoration: line-through" - SubscriptPlugin:
<sub>或style="vertical-align: sub" - SuperscriptPlugin:
<sup>或style="vertical-align: super" - CodePlugin:
<code>或style="font-family: Consolas"(不在<pre>标签内) - KbdPlugin:
<kbd>
段落和标题
- ParagraphPlugin:
<p> - HeadingPlugin:
<h1>、<h2>、<h3>、<h4>、<h5>、<h6>
列表
- ListPlugin:
- 无序列表:
<ul> - 有序列表:
<ol> - 列表项:
<li>
- 无序列表:
- IndentListPlugin:
- 列表项:
<li> - 解析
aria-level属性以确定缩进
- 列表项:
块级元素
- BlockquotePlugin:
<blockquote> - CodeBlockPlugin:
- 反序列化
<pre>元素 - 反序列化带有
fontFamily: 'Consolas'样式的<p>元素 - 将内容分割成代码行
- 如果可用,保留语言信息
- 反序列化
- HorizontalRulePlugin:
<hr>
链接和媒体
- LinkPlugin:
<a> - ImagePlugin:
<img> - MediaEmbedPlugin:
<iframe>
表格
- TablePlugin:
- 表格:
<table> - 表格行:
<tr> - 表格单元格:
<td> - 表格标题单元格:
<th>
- 表格:
文本样式
- FontBackgroundColorPlugin:
style="background-color: *" - FontColorPlugin:
style="color: *" - FontFamilyPlugin:
style="font-family: *" - FontSizePlugin:
style="font-size: *" - FontWeightPlugin:
style="font-weight: *" - HighlightPlugin:
<mark>
布局和格式
- AlignPlugin:
style="text-align: *" - LineHeightPlugin:
style="line-height: *"
反序列化属性
插件可以定义各种属性来控制 HTML 反序列化:
parse: 将 HTML 元素解析为 Plate 节点的函数query: 确定是否应用反序列化器的函数rules: 定义有效 HTML 元素和属性的规则对象数组isElement: 指示插件是否反序列化元素isLeaf: 指示插件是否反序列化叶子节点attributeNames: 要存储在node.attributes中的 HTML 属性名列表withoutChildren: 从反序列化中排除子节点rules: 用于元素匹配的规则对象数组validAttribute: 有效的元素属性validClassName: 有效的 CSS 类名validNodeName: 有效的 HTML 标签名validStyle: 有效的 CSS 样式
扩展反序列化
你可以扩展或自定义任何插件的反序列化行为。以下是如何扩展 CodeBlockPlugin 的示例:
import { CodeBlockPlugin } from '@udecode/plate-code-block/react';
const CustomCodeBlockPlugin = CodeBlockPlugin.extend({
parsers: {
html: {
deserializer: {
parse: ({ element }) => {
const language = element.getAttribute('data-language');
const textContent = element.textContent || '';
const lines = textContent.split('\n');
return {
type: CodeBlockPlugin.key,
language,
children: lines.map((line) => ({
type: CodeLinePlugin.key,
children: [{ text: line }],
})),
};
},
rules: [
...CodeBlockPlugin.parsers.html.deserializer.rules,
{ validAttribute: 'data-language' },
],
},
},
},
});此自定义添加了对代码块反序列化中 data-language 属性的支持,并保留了语言信息。
高级反序列化示例
IndentListPlugin 提供了一个更复杂的反序列化过程:
- 它将 HTML 列表结构转换为缩进的段落。
- 它通过保留缩进级别来处理嵌套列表。
- 它使用
aria-level属性来确定缩进级别。
以下是其反序列化逻辑的简化版本:
export const IndentListPlugin = createTSlatePlugin<IndentListConfig>({
// ... other configurations ...
parsers: {
html: {
deserializer: {
isElement: true,
parse: ({ editor, element, getOptions }) => ({
indent: Number(element.getAttribute('aria-level')),
listStyleType: element.style.listStyleType,
type: editor.getType(ParagraphPlugin),
}),
rules: [
{
validNodeName: 'LI',
},
],
},
},
},
});API
editor.api.html.deserialize
将 HTML 字符串反序列化为 Slate 值。
Parameters
- 默认值:
true(将删除空白。)
options.element HTMLElement | string
要反序列化的 HTML 元素或字符串。
options.collapseWhiteSpace optional boolean
用于启用或禁用从序列化 HTML 中删除空白的标志。
Returns
反序列化后的 Slate 值。