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 值。