docs/(plugins)/(serializing)/markdown.cn.mdx
@platejs/markdown 提供了强大的 Plate 内容结构与 Markdown 之间的双向转换能力。
deserialize 方法)。serialize 方法)。dangerouslySetInnerHTML,安全可靠。rules 配置自定义特定 Markdown 语法或 Plate 元素的转换方式,支持 MDX。remarkPlugins 选项集成 remark 插件。remark-gfm 启用 GFM(GitHub 风格 Markdown 扩展)。与 react-markdown 等库仅将 Markdown 渲染成 React 元素不同,@platejs/markdown 深度集成 Plate 体系,带来如下优势:
最快速增加 Markdown 支持的方法是使用 MarkdownKit,它已预配置好带必要 remark 插件的 MarkdownPlugin,兼容 Plate UI。
import { createPlateEditor } from 'platejs/react';
import { MarkdownKit } from '@/components/editor/plugins/markdown-kit';
const editor = createPlateEditor({
plugins: [
// ...其它插件,
...MarkdownKit,
],
});
npm install platejs @platejs/markdown
import { MarkdownPlugin } from '@platejs/markdown';
import { createPlateEditor } from 'platejs/react';
const editor = createPlateEditor({
plugins: [
// ...其它插件,
MarkdownPlugin,
],
});
推荐通过 MarkdownPlugin 的 configure 方法配置粘贴支持与自定义转换规则。
import { createPlateEditor } from 'platejs/react';
import { MarkdownPlugin, remarkMention, remarkMdx } from '@platejs/markdown';
import remarkGfm from 'remark-gfm';
import remarkMath from 'remark-math';
const editor = createPlateEditor({
plugins: [
// ...其它 Plate 插件
MarkdownPlugin.configure({
options: {
// 通过 remarkPlugins 添加语法扩展(如 GFM、数学、MDX)
remarkPlugins: [remarkMath, remarkGfm, remarkMdx, remarkMention],
// 如需自定义转换规则可配置 rules
rules: {
// date: { /* ... 规则对象 ... */ },
},
},
}),
],
});
// 如需禁用 Markdown 粘贴处理:
const editorWithoutPaste = createPlateEditor({
plugins: [
// ...其它 Plate 插件
MarkdownPlugin.configure(() => ({ parser: null })),
],
});
用 editor.api.markdown.deserialize 方法将 Markdown 字符串转换为 Plate Value(节点数组),常用于设置编辑器初始内容。
import { createPlateEditor } from 'platejs/react';
import { MarkdownPlugin } from '@platejs/markdown';
// ...还需引入其它用于渲染的 Plate 插件
const markdownString = '# Hello, *Plate*!';
const editor = createPlateEditor({
plugins: [
// 必须包含 MarkdownPlugin
MarkdownPlugin,
// ...渲染反序列化元素所需的其它插件(如 HeadingPlugin、ItalicPlugin)
],
// 初始内容通过反序列化生成
value: (editor) =>
editor.getApi(MarkdownPlugin).markdown.deserialize(markdownString),
});
用 editor.api.markdown.serialize 将当前编辑器内容或指定节点数组转回 Markdown 字符串。
序列化当前编辑器内容:
// 假设 editor 已有内容
const markdownOutput = editor.api.markdown.serialize();
console.info(markdownOutput);
序列化指定节点数组:
const specificNodes = [
{ type: 'p', children: [{ text: '仅序列化这一段。' }] },
{ type: 'h1', children: [{ text: '以及这个标题。' }] },
];
// 假设 editor 是你的 Plate 实例
const partialMarkdownOutput = editor.api.markdown.serialize({
value: specificNodes,
});
console.info(partialMarkdownOutput);
核心特性之一是支持无标准 Markdown 语法的自定义 Plate 元素(如下划线、提及等),@platejs/markdown 会在序列化时转为 MDX 元素,并可无损还原。
示例:处理自定义 date 元素
Plate 节点结构:
{
type: 'p',
children: [
{ text: 'Today is ' },
{ type: 'date', date: '2025-03-31', children: [{ text: '' }] } // 叶节点需有 text
],
}
通过 rules 配置插件:
import type { MdMdxJsxTextElement } from '@platejs/markdown';
import { MarkdownPlugin, remarkMdx } from '@platejs/markdown';
// ... 其它导入
MarkdownPlugin.configure({
options: {
rules: {
// 键名匹配规则:
// 1. Plate 插件的 key 或 type
// 2. mdast 节点类型
// 3. MDX tag 名
date: {
// Markdown -> Plate
deserialize(mdastNode: MdMdxJsxTextElement, deco, options) {
const dateValue = (mdastNode.children?.[0] as any)?.value || '';
return {
type: 'date', // 你的 Plate 节点类型
date: dateValue,
children: [{ text: '' }], // 合法 Plate 结构
};
},
// Plate -> Markdown(MDX)
serialize: (slateNode): MdMdxJsxTextElement => {
return {
type: 'mdxJsxTextElement',
name: 'date', // MDX 标签名
attributes: [], // 可选,如 [{ type: 'mdxJsxAttribute', name: 'date', value: slateNode.date }]
children: [{ type: 'text', value: slateNode.date || '1999-01-01' }],
};
},
},
// ...其它自定义元素规则
},
remarkPlugins: [remarkMdx /*, 其它如 remarkGfm 的插件 */],
},
});
转换流程说明:
date 节点会转为 <date>2025-03-31</date>。<date>2025-03-31</date> 可还原对应 Plate date 节点。MarkdownPlugin核心插件配置对象。使用 MarkdownPlugin.configure({ options: {} }) 可以设置全局的 Markdown 处理选项。
api.markdown.deserialize将 Markdown 字符串转换为 Plate Value(Descendant[])。
api.markdown.serialize将 Plate Value(Descendant[])序列化为 Markdown 字符串。
parseMarkdownBlocks工具函数:将 Markdown 字符串切分为块级 token(deserialize 内部使用,memoize 时也有用)。
为编辑器增加 GitHub Flavored Markdown(GFM)支持,包括表格、删除线、任务列表和自动链接。
插件配置:
import { createPlateEditor } from 'platejs/react';
import { MarkdownPlugin } from '@platejs/markdown';
import remarkGfm from 'remark-gfm';
// 导入 GFM 相关 Plate 插件
import { TablePlugin } from '@platejs/table/react';
import { TodoListPlugin } from '@platejs/list-classic/react'; // 对应任务列表的 List 插件
import { StrikethroughPlugin } from '@platejs/basic-nodes/react';
import { LinkPlugin } from '@platejs/link/react';
const editor = createPlateEditor({
plugins: [
// ...其他插件
TablePlugin,
TodoListPlugin, // 或你当前使用的任务列表插件
StrikethroughPlugin,
LinkPlugin,
MarkdownPlugin.configure({
options: {
remarkPlugins: [remarkGfm],
},
}),
],
});
用法示例:
const markdown = `
A table:
| a | b |
| - | - |
~~Strikethrough~~
- [x] Task list item
Visit https://platejs.org
`;
// 假设 `editor` 是已配置好的 Plate 编辑器实例
const slateValue = editor.api.markdown.deserialize(markdown);
// editor.tf.setValue(slateValue); // 可以设置编辑器内容
const markdownOutput = editor.api.markdown.serialize();
// markdownOutput 将包含 GFM 语法
本例展示两种方式:一种是自定义渲染组件(适合仅 UI 层变更),另一种是自定义转换规则(适合改变 Plate 数据结构)。
背景说明:
@platejs/markdown 会将 Markdown 的代码块(如 ```js ... ```)转换为 Plate 的 code_block 元素,子元素为 code_line。CodeBlockElement(通常来自 @platejs/code-block/react)负责渲染这一结构。CodeBlockElement 渲染中通过 lowlight(由 CodeBlockPlugin 提供)实现。详见 代码块插件文档。方式一:自定义渲染组件(推荐 UI 层改动时使用)
自定义 code_block 插件的渲染组件即可更改代码块的外观。
import { createPlateEditor } from 'platejs/react';
import {
CodeBlockPlugin,
CodeLinePlugin,
CodeSyntaxPlugin,
} from '@platejs/code-block/react';
import { MarkdownPlugin } from '@platejs/markdown';
import { MyCustomCodeBlockElement } from './my-custom-code-block'; // 你的自定义组件
const editor = createPlateEditor({
plugins: [
CodeBlockPlugin.withComponent(MyCustomCodeBlockElement), // 基础插件 + 自定义渲染
CodeLinePlugin.withComponent(MyCustomCodeLineElement),
CodeSyntaxPlugin.withComponent(MyCustomCodeSyntaxElement),
MarkdownPlugin, // 用于 Markdown 转换
// ... 其他插件
],
});
// MyCustomCodeBlockElement.tsx 内实现自定义渲染(如用 react-syntax-highlighter),并消费 PlateElement 的 props。
完整用法详见 代码块插件文档。
方式二:自定义转换规则(高级用法 - Plate 数据结构变更)
如果希望代码块以单独字符串属性存储(非 code_line 拆分),可重写 deserialize 规则。
import { MarkdownPlugin } from '@platejs/markdown';
import { CodeBlockPlugin } from '@platejs/code-block/react';
MarkdownPlugin.configure({
options: {
rules: {
// 自定义 mdast 的 'code' 类型反序列化方式
code: {
deserialize: (mdastNode, deco, options) => {
return {
type: KEYS.codeBlock, // Plate 的 type
lang: mdastNode.lang ?? undefined,
rawCode: mdastNode.value || '', // 直接存原始 code 文本
children: [{ text: '' }], // Plate 元素必须要有子文本节点
};
},
},
// 还需要为 `code_block` 自定义 serialize 规则,将 `rawCode` 转回 mdast 'code' 节点
[KEYS.codeBlock]: {
serialize: (slateNode, options) => {
return {
// mdast 'code' 节点
type: 'code',
lang: slateNode.lang,
value: slateNode.rawCode,
};
},
},
},
// remarkPlugins: [...]
},
});
// 你的自定义渲染组件(MyCustomCodeBlockElement)应读取 `rawCode` 属性
可根据需求选择 UI 层调整(方式一)或底层数据结构调整(方式二)。
remark-math)支持 TeX 数学语法(如 $行内$、$$块级$$)。
插件配置:
import { createPlateEditor } from 'platejs/react';
import { MarkdownPlugin } from '@platejs/markdown';
import remarkMath from 'remark-math';
// Plate 数学渲染相关插件
import { MathPlugin } from '@platejs/math/react'; // 主 Math 插件
const editor = createPlateEditor({
plugins: [
// ...其他插件
MathPlugin, // 渲染块级与行内公式
MarkdownPlugin.configure({
options: {
remarkPlugins: [remarkMath],
// 内置规则已支持 remark-math 产生的 'math' / 'inlineMath' mdast 节点到 Plate 的 'equation' 和 'inline_equation'
},
}),
],
});
用法示例:
const markdown = `
Inline math: $E=mc^2$
Block math:
$$
\\int_a^b f(x) dx = F(b) - F(a)
$$
`;
// 假设 `editor` 为已配置 Plate 编辑器实例
const slateValue = editor.api.markdown.deserialize(markdown);
// slateValue 包含 'inline_equation' 及 'equation' 节点
const markdownOutput = editor.api.markdown.serialize({ value: slateValue });
// 输出 Markdown 字符串包含 $...$ 和 $$...$$ 语法
remarkMention)使用链接语法风格的 mention,兼容多语言和特殊字符,序列化时格式始终如 [显示文本](mention:id)。
插件配置:
import { createPlateEditor } from 'platejs/react';
import { MarkdownPlugin, remarkMention } from '@platejs/markdown';
import { MentionPlugin } from '@platejs/mention/react';
const editor = createPlateEditor({
plugins: [
// ...其他插件
MentionPlugin,
MarkdownPlugin.configure({
options: {
remarkPlugins: [remarkMention],
},
}),
],
});
支持的 Markdown 格式:
const markdown = `
Mention: [Alice](mention:alice)
Mention with spaces: [John Doe](mention:john_doe)
Full name with ID: [Jane Smith](mention:user_123)
`;
// 假设 `editor` 是已配置好的 Plate 编辑器
const slateValue = editor.api.markdown.deserialize(markdown);
// 自动生成合适值与显示文本的 mention 节点
const markdownOutput = editor.api.markdown.serialize({ value: slateValue });
// mention 序列化后始终用链接格式: [Alice](mention:alice), [John Doe](mention:john_doe) 等
remarkMention 插件采用 显示文本 的链接语法,支持带空格与自定义显示文本。
序列化时所有 mention 都用同一链接风格,以充分支持特殊字符与一致性。
支持多列文档,可通过 MDX 语法定义多列结构。
插件配置:
import { createPlateEditor } from 'platejs/react';
import { MarkdownPlugin, remarkMdx } from '@platejs/markdown';
import { ColumnPlugin, ColumnItemPlugin } from '@platejs/layout/react';
const editor = createPlateEditor({
plugins: [
// ...其他插件
ColumnPlugin,
ColumnItemPlugin,
MarkdownPlugin.configure({
options: {
remarkPlugins: [remarkMdx], // 多列 MDX 语法需要
},
}),
],
});
支持的 Markdown 格式:
const markdown = `
<column_group>
<column width="50%">
左侧内容,宽度 50%
</column>
<column width="50%">
右侧内容,宽度 50%
</column>
</column_group>
<column_group>
<column width="33%">第一列</column>
<column width="33%">第二列</column>
<column width="34%">第三列</column>
</column_group>
`;
// 假设 `editor` 为已配置好的 Plate 编辑器
const slateValue = editor.api.markdown.deserialize(markdown);
// 生成包含嵌套 column 元素的 column_group 节点
const markdownOutput = editor.api.markdown.serialize({ value: slateValue });
// 输出的 Markdown 保持列结构和宽度属性
多列特性:
@platejs/markdown 基于 unified 与 remark 生态。通过在 MarkdownPlugin.configure 的 remarkPlugins 选项中添加 remark 插件可扩展能力。这些插件对 mdast(Markdown 抽象语法树) 进行操作。
插件查找指南:
常见用途:
remark-gfm(表格等)、remark-math(TeX)、remark-frontmatter、remark-mdxremark-lint(通常作为独立工具链)@platejs/markdown 使用 remark-parse,遵循 CommonMark 标准。可通过 remarkPlugins 开启 GFM 或其它语法扩展。
@platejs/markdown 基于 unified/remark,将 Markdown 字符串与 Plate 编辑器格式进行互转。
@platejs/markdown
+--------------------------------------------------------------------------------------------+
| |
| +-----------+ +----------------+ +---------------+ +-----------+ |
| | | | | | | | | |
markdown-+->+ remark +-mdast->+ remark plugins +-mdast->+ mdast-to-slate+----->+ nodes +-plate-+->react elements
| | | | | | | | | |
| +-----------+ +----------------+ +---------------+ +-----------+ |
| ^ | |
| | v |
| +-----------+ +----------------+ +---------------+ +-----------+ |
| | | | | | | | | |
| | stringify |<-mdast-+ remark plugins |<-mdast-+ slate-to-mdast+<-----+ serialize | |
| | | | | | | | | |
| +-----------+ +----------------+ +---------------+ +-----------+ |
| |
+--------------------------------------------------------------------------------------------+
关键流程:
remark-parse → mdastremarkPlugins 变换 mdast(如 remark-gfm)mdast-to-slate 利用 rules 转化为 Plate 节点slate-to-mdast(用 rules)→ mdastremarkPlugins 变换 mdastremark-stringify 输出为 Markdown 字符串react-markdown 迁移迁移时需将 react-markdown 的概念映射到 Plate 体系。
主要区别:
react-markdown(MD → mdast → hast → React) VS @platejs/markdown(MD ↔ mdast ↔ Plate JSON,Plate 组件直接渲染 Plate 节点)react-markdown:components 属性替换 HTML 节点渲染MarkdownPlugin 提供 rules 定制 mdast↔Plate JSON 互转createPlateEditor 的 components 配置 Plate 节点的渲染组件(详见附录C)@platejs/markdown 主要以 remarkPlugins 为核心,rehypePlugins 使用较少对照表:
react-markdown 属性 | @platejs/markdown 等价项/概念 | 备注 |
|---|---|---|
children (字符串) | 传递给 editor.api.markdown.deserialize(string) | 通常在 createPlateEditor 的 value 配置 |
remarkPlugins | MarkdownPlugin.configure({ options: { remarkPlugins: [...] }}) | 直接映射;在 mdast 层操作 |
rehypePlugins | 通常不需要,如有需求通过 remarkPlugins 完成语法扩展 | Plate 组件自行渲染。原生 HTML 需求通过 rehype-raw 加入 |
components={{ h1: MyH1 }} | createPlateEditor({ components: { h1: MyH1 } }) | 设置对应渲染组件,h1 等键依赖 HeadingPlugin 配置 |
components={{ code: MyCode }} | ① 转换规则:MarkdownPlugin > rules > code ② 渲染:components: { [KEYS.codeBlock]: MyCode } | rules 处理 mdast(code)到 Plate(code_block);自定义渲染组件 |
allowedElements | MarkdownPlugin.configure({ options: { allowedNodes: [...] }}) | 转换时过滤 mdast/Plate 节点 |
disallowedElements | MarkdownPlugin.configure({ options: { disallowedNodes: [...] }}) | 转换时过滤不支持节点 |
unwrapDisallowed | 无直接等价项,默认过滤节点 | 可通过自定义 rules 实现展开逻辑 |
skipHtml | 默认会移除大部分 HTML | 如需保留,使用 remarkPlugins 引入 rehype-raw |
urlTransform | 在 rules 对 link 处理,或按插件类型序列化处理 | 在转换规则中自定义实现 |
allowElement | MarkdownPlugin.configure({ options: { allowNode: { ... } } }) | 转换时自定义函数过滤节点 |
默认情况下,@platejs/markdown 出于安全考虑不会处理原始的 HTML 标签。标准 Markdown 生成的 HTML(如 *emphasis* → <em>)会被正常转换。
如需在受信任环境下处理原始 HTML:
remark-mdx: 添加到 remarkPlugins。rehype-raw: 将rehype-raw添加进remarkPlugins。hast 节点自定义rules,将其映射到 Plate 结构。import { MarkdownPlugin, remarkMdx } from '@platejs/markdown';
import rehypeRaw from 'rehype-raw'; // 可能需要 VFile,确保兼容性
// import { VFile } from 'vfile'; // 如果 rehype-raw 需要 VFile
MarkdownPlugin.configure({
options: {
remarkPlugins: [
remarkMdx,
// 在 remark 流水线中使用 rehype 插件会比较复杂
[
rehypeRaw,
{
/* 配置项,如传递 vfile */
},
],
],
rules: {
// 示例:对 rehype-raw 解析出来的 HTML 标签设置规则
// mdastNode 结构取决于 rehype-raw 的输出
element: {
// 针对 rehype-raw 产出的“element”节点的通用处理
deserialize: (mdastNode, deco, options) => {
// 简化示例:请根据 mdastNode.tagName 及其属性做完整处理
// 实际上常需要针对每一种 HTML 标签做独立规则
if (mdastNode.tagName === 'div') {
return {
type: 'html_div', // 例如:映射到 Plate 的自定义元素 'html_div'
children: convertChildrenDeserialize(
mdastNode.children,
deco,
options
),
};
}
// 其他标签回退处理
return convertChildrenDeserialize(mdastNode.children, deco, options);
},
},
// 如需要将Plate结构重新序列化为原始HTML,请补充相应规则
},
},
});
rules)在 MarkdownPlugin.configure 中设置的 rules 选项,提供了 mdast ↔ Plate JSON 转换的精细控制。rules 对象中的字段需与各节点类型对应。
mdast 节点类型(如 paragraph、heading、strong、link,以及 MDX 节点如 mdxJsxTextElement)。deserialize 函数接收 (mdastNode, deco, options),返回 Plate 的 Descendant 或 Descendant[]。p、h1、a、code_block、bold)。serialize 函数接收 (slateNode, options),返回 mdast 节点。示例:自定义链接反序列化规则
MarkdownPlugin.configure({
options: {
rules: {
// 针对 mdast 的 'link' 类型的规则
link: {
deserialize: (mdastNode, deco, options) => {
// 默认会生成 { type: 'a', url: ..., children: [...] }
// 这里添加一个自定义属性
return {
type: 'a', // Plate 链接节点类型
url: mdastNode.url,
title: mdastNode.title,
customProp: 'added-during-deserialize',
children: convertChildrenDeserialize(
mdastNode.children,
deco,
options
),
};
},
},
// 如序列化时需处理 customProp,可对 Plate 的 'a' 类型覆写规则
a: {
// 假定 'a' 就是 Plate 链接类型
serialize: (slateNode, options) => {
// 默认输出 mdast 的 'link'
// 如需将 customProp 输出为 MDX 属性等,可自定义处理
return {
type: 'link', // mdast 类型
url: slateNode.url,
title: slateNode.title,
// customProp: slateNode.customProp, // MDX 属性占位?
children: convertNodesSerialize(slateNode.children, options),
};
},
},
},
// ... 其他 remarkPlugins 配置 ...
},
});
默认规则概览
完整内容可查看 defaultRules.ts。主要规则概览如下:
| Markdown(mdast) | Plate 类型 | 备注 |
|---|---|---|
paragraph | p | |
heading (depth) | h1 - h6 | 根据 depth 自动映射 |
blockquote | blockquote | |
有序 list | ol / p* | ol/li/lic;或通过 p+列表缩进属性 |
无序 list | ul / p* | ul/li/lic;或通过 p+列表缩进属性 |
code (fenced) | code_block | 包裹 code_line 子节点 |
inlineCode | code (mark) | 应用于文本 |
strong | bold (mark) | 应用于文本 |
emphasis | italic (mark) | 应用于文本 |
delete | strikethrough(mark) | 应用于文本 |
link | a | |
image | img | 序列化时会包裹在段落 |
thematicBreak | hr | |
table | table | 子节点为 tr |
math (block) | equation | 需配合 remark-math 使用 |
inlineMath | inline_equation | 需配合 remark-math 使用 |
mdxJsxFlowElement | 自定义 | 需配合 remark-mdx,并补充自定义规则 |
mdxJsxTextElement | 自定义 | 需配合 remark-mdx,并补充自定义规则 |
* 列表类型的转换依赖于是否启用 ListPlugin
默认 MDX 转换(配合 remark-mdx):
| MDX(mdast) | Plate 类型 | 备注 |
|---|---|---|
<del>...</del> | strikethrough (mark) | 另一种写法 ~~strikethrough~~ |
<sub>...</sub> | subscript (mark) | H<sub>2</sub>O |
<sup>...</sup> | superscript (mark) | E=mc<sup>2</sup> |
<u>...</u> | underline (mark) | <u>下划线文本</u> |
<mark>...</mark> | highlight (mark) | <mark>高亮文本</mark> |
<span style="font-family: ..."> | fontFamily (mark) | |
<span style="font-size: ..."> | fontSize (mark) | |
<span style="font-weight: ..."> | fontWeight (mark) | |
<span style="color: ..."> | color (mark) | |
<span style="background-color: ..."> | backgroundColor (mark) | |
<date>...</date> | date | 自定义日期元素 |
[text](mention:id) | mention | 自定义提及元素 |
<file name="..." /> | file | 自定义文件元素 |
<audio src="..." /> | audio | 自定义音频元素 |
<video src="..." /> | video | 自定义视频元素 |
<toc /> | toc | 目录 |
<callout>...</callout> | callout | 提示块 |
<column_group>...</column_group> | column_group | 多列布局容器 |
<column width="50%">...</column> | column | 单列,可指定宽度属性 |
rules 主要负责 MD ↔ Plate 的数据转换,而 Plate 实际渲染节点时,使用 React 组件。可以通过 createPlateEditor 的 components 选项或插件的 withComponent 方法配置。
示例:
import { createPlateEditor, ParagraphPlugin, PlateLeaf } from 'platejs/react';
import { BoldPlugin } from '@platejs/basic-nodes/react';
import { CodeBlockPlugin } from '@platejs/code-block/react';
import { ParagraphElement } from '@/components/ui/paragraph-node'; // UI组件示例
import { CodeBlockElement } from '@/components/ui/code-block-node'; // UI组件示例
const editor = createPlateEditor({
plugins: [
ParagraphPlugin.withComponent(ParagraphElement),
CodeBlockPlugin.withComponent(CodeBlockElement),
BoldPlugin,
/* ... 其他插件 ... */
],
});
更多自定义与注册方式请参考 插件组件相关文档。
PlateMarkdown 只读展示组件如果需要类似 react-markdown 的只读渲染组件,可以参考下方示例:
import React, { useEffect } from 'react';
import { Plate, PlateContent, usePlateEditor } from 'platejs/react';
import { MarkdownPlugin } from '@platejs/markdown';
// 导入各类常用的 Plate 插件
import { HeadingPlugin } from '@platejs/basic-nodes/react';
// ... 还可以按需引入 BlockquotePlugin、CodeBlockPlugin、ListPlugin等
// ... 以及粗体、斜体等标记插件
export interface PlateMarkdownProps {
children: string; // Markdown 内容
remarkPlugins?: any[];
components?: Record<string, React.ComponentType<any>>; // Plate 渲染组件(可选)
className?: string;
}
export function PlateMarkdown({
children,
remarkPlugins = [],
components = {},
className,
}: PlateMarkdownProps) {
const editor = usePlateEditor({
plugins: [
// 加入渲染 Markdown 所需的所有插件
HeadingPlugin /* ... 其他插件 ... */,
MarkdownPlugin.configure({ options: { remarkPlugins } }),
],
components, // 传递自定义渲染组件
});
useEffect(() => {
editor.tf.reset(); // 清空先前内容
editor.tf.setValue(
editor.getApi(MarkdownPlugin).markdown.deserialize(children)
);
}, [children, editor, remarkPlugins]); // 当 Markdown 内容或插件更改时重新反序列化
return (
<Plate editor={editor}>
<PlateContent readOnly className={className} />
</Plate>
);
}
// 使用示例:
// const markdownString = "# Hello\nThis is *Markdown*.";
// <PlateMarkdown className="prose dark:prose-invert">
// {markdownString}
// </PlateMarkdown>
@platejs/markdown 优先保证渲染安全,会把 Markdown 转换成结构化的 Plate 格式,避免直接渲染 HTML。但安全性依赖于:
rules: 保证自定义的 deserialize 规则不会引入不安全数据。remarkPlugins: 谨慎甄别使用的第三方 remark 插件,注意其安全性隐患。rehype-raw,必须在来源不可信时联合 rehype-sanitize 进行清洗。isUrl 说明,或媒体插件的 parseMediaUrl。建议: 对于不可信的 Markdown 输入务必格外谨慎。如果开放复杂扩展或原始 HTML 功能,请做好输入清洗。