Lexical Markdown 集成指南:@lexical/markdown 的导入导出、快捷键与 Transformers 深度解析
Lexical Markdown 集成指南lexical/markdown 的导入导出、快捷键与 Transformers 深度解析【免费下载链接】lexicalLexical is an extensible text editor framework that provides excellent reliability, accessibility and performance.项目地址: https://gitcode.com/GitHub_Trending/le/lexicallexical/markdown是 Lexical 官方提供的 Markdown 辅助包为富文本编辑器带来完整的 Markdown 导入import、导出export与输入快捷键shortcuts能力。本文以 packages/lexical-markdown/README.md 为主线结合仓库源码深入讲解其核心 API、内置 Transformers 的构成与顺序规则、自定义 Transformer 的接口契约以及导入导出管线的底层实现帮助你在自己的 Lexical 应用中落地复制粘贴 Markdown / 以 Markdown 初始化 / 输入时实时排版三类典型场景。一、包定位与能力概览lexical/markdown包的定位非常纯粹它不包含任何编辑器 UI只提供三类能力Import导入把 Markdown 字符串解析成 Lexical 节点树写入编辑器状态Export导出把编辑器状态序列化回 Markdown 字符串整篇或仅选中内容Shortcuts快捷键用户在编辑器内输入#、-、**等 Markdown 标记时实时转换为对应富文本节点。从 package.json 可以看到它依赖lexical以及lexical/code-core、lexical/link、lexical/list、lexical/rich-text、lexical/selection、lexical/text、lexical/utils、lexical/internal等包说明 Markdown 转换最终映射到的是 Lexical 的 Code、Link、List、Heading、Quote 等标准节点体系。二、导入与导出四个核心函数包的公共入口在 packages/lexical-markdown/src/index.ts导出了四个核心函数覆盖整篇转换与选中内容转换两个维度。2.1 导出$convertToMarkdownString将当前编辑器状态或指定的ElementNode子树导出为 Markdown 字符串import { $convertToMarkdownString, TRANSFORMERS, } from lexical/markdown; editor.update(() { const markdown $convertToMarkdownString(TRANSFORMERS); // 得到整篇文档的 Markdown 文本 });函数签名来自 index.tsfunction $convertToMarkdownString( transformers: Transformer[] TRANSFORMERS, node?: ElementNode, // 不传则导出整篇根节点 shouldPreserveNewLines: boolean false, ): string其中shouldPreserveNewLines为true时转换过程会保留源码中的换行结构同时源码注释指出保留换行时还会对* _ ~等特殊字符做转义避免破坏 Markdown 语义见 MarkdownExport.ts 中exportTextFormat的转义逻辑。2.2 导入$convertFromMarkdownString将 Markdown 字符串解析为节点并写入编辑器根节点操作完成后选区移到文档开头editor.update(() { $convertFromMarkdownString(markdown, TRANSFORMERS); });函数签名来自 index.tsfunction $convertFromMarkdownString( markdown: string, transformers: Transformer[] TRANSFORMERS, node?: ElementNode, // 不传则写入 $getRoot() shouldPreserveNewLines false, shouldMergeAdjacentLines false, // CommonMark 相邻非空行合并规则 ): void两个布尔参数的行为差异值得注意源码注释有明确说明shouldPreserveNewLines true保留 Markdown 源文本中的换行例如空段落、硬换行等不会被吞掉shouldPreserveNewLines false时shouldMergeAdjacentLines才生效相邻的非空行会按 CommonMark 规范spec.commonmark.org 0.24 第 177 例合并为同一段落。合并与保留的具体逻辑在normalizeMarkdown中实现见 MarkdownTransformers.ts它逐行扫描能识别代码围栏围栏内的行一律原样保留、标题、引用、列表、表格分隔行等块级结构在这些结构之间以及空行处不合并其余相邻文本行用空格拼接。2.3 生成节点但不落树$generateNodesFromMarkdownString该函数解析 Markdown 后返回LexicalNode[]数组不修改文档树、不触碰选区返回的节点可以借助selection.insertNodes()插入到任意位置源码注释原文说明。其实现是先把节点导入到一个ArtificialNode__DO_NOT_USE临时容器再取出子节点非常适合把 Markdown 片段插入到光标处这类需求index.ts。2.4 选中内容导出$convertSelectionToMarkdownString只把当前选区内容导出为 Markdownfunction $convertSelectionToMarkdownString( transformers: Transformer[] TRANSFORMERS, selection: BaseSelection | null, shouldPreserveNewLines: boolean false, ): string当selection为空或是折叠选区collapse时直接返回空字符串index.ts。其底层由createSelectionMarkdownExport实现MarkdownExport.ts采用类似 HTML 导出中extractWithChild的递归结构正确处理链接等行内元素上的部分选区例如选中一行列表项时未选中的兄弟项会被跳过。三、用 Markdown 初始化编辑器状态导入 API 最常见的实战场景之一是用 Markdown 字符串初始化编辑器的初始内容。README 给出了配合 ReactRichTextPlugin的标准写法LexicalComposer initialConfig{{ editorState: () $convertFromMarkdownString(markdown, TRANSFORMERS), }} RichTextPlugin / /LexicalComposereditorState是惰性求值的函数在编辑器首次渲染时执行一次即可把 Markdown 渲染为富文本内容。这在从数据库/文件恢复内容预览 Markdown 文件等场景中非常实用。四、输入快捷键边打字边转 Markdown4.1 React 场景MarkdownShortcutPlugin如果使用 React直接挂载官方提供的插件组件即可import { TRANSFORMERS } from lexical/markdown; import { MarkdownShortcutPlugin } from lexical/react/LexicalMarkdownShortcutPlugin; LexicalComposer MarkdownShortcutPlugin transformers{TRANSFORMERS} / /LexicalComposer此后用户输入#生成一级标题、##生成二级标题、-生成无序列表、1.生成有序列表、生成引用、生成代码块、**包裹生成加粗、text生成链接等。4.2 非 React 场景registerMarkdownShortcuts不用 React 时可以通过registerMarkdownShortcuts手动注册快捷键监听import { registerMarkdownShortcuts, TRANSFORMERS } from lexical/markdown; const editor createEditor(/* ... */); registerMarkdownShortcuts(editor, TRANSFORMERS);该函数实现在 MarkdownShortcuts.ts核心机制值得展开通过registerCommand注册文本变化TEXT_INSERT_COMMAND监听在每次输入后判断光标前的文本是否命中 Transformer 的正则对于块级elementTransformer要求锚点字符是段首文本节点、且光标前一字符为空格防止在单词中间误触发命中后执行splitText分割并调用replace完成节点替换MarkdownShortcuts.ts文本匹配text-matchTransformer如链接则通过trigger字符如)在敲入该字符的瞬间触发匹配部分块级 Transformer 还支持triggerOnEnter: true即行尾直接按回车也能触发无需尾随空格内置的HEADING、QUOTE、UNORDERED_LIST、ORDERED_LIST、CHECK_LIST均开启了该选项见 MarkdownTransformers.ts 的HEADING定义。五、Transformers一切转换的灵魂Markdown 的一切功能都建立在transformers 配置数组之上。它是一个对象数组定义了在导入、导出或输入过程中如何处理特定文本或节点。README 原文强调Transformers are explicitly passed to markdown API allowing application-specific subset of markdown or custom transformers——即 transformers 由调用方显式传入你可以自由裁剪内置集合也可以编写自定义 Transformer。5.1 三种 Transformer 类型类型作用对象典型代表源码类型定义位置Element transformer顶层块级元素列表、标题、引用、表格、代码块HEADING、QUOTE、UNORDERED_LIST、ORDERED_LISTMarkdownTransformers.tsText format transformer应用TextFormatType定义的文本范围格式BOLD_STAR、ITALIC_STAR、INLINE_CODE、STRIKETHROUGHMarkdownTransformers.tsText match transformer匹配叶子文本节点的内容并替换为节点LINKMarkdownTransformers.ts三种类型对应源码中的联合类型Transformer ElementTransformer | MultilineElementTransformer | TextFormatTransformer | TextMatchTransformerMarkdownTransformers.ts。5.2 内置 Transformers 清单README 列出了包内提供的全部内置 TransformerElement transformers块级UNORDERED_LIST // 无序列表- * 开头 CODE // 代码块 围栏 HEADING // 标题# ~ ###### ORDERED_LIST // 有序列表1. 2. ... QUOTE // 引用 开头Text format transformers文本格式BOLD_ITALIC_STAR // ***text*** BOLD_ITALIC_UNDERSCORE // ___text___ BOLD_STAR // **text** BOLD_UNDERSCORE // __text__ INLINE_CODE // code ITALIC_STAR // *text* ITALIC_UNDERSCORE // _text_ STRIKETHROUGH // ~~text~~Text match transformers文本匹配LINK // text此外在 index.ts 的导出中还可以看到两个 README 清单之外的内置项CHECK_LIST任务清单匹配- [ ]/- [x]属于 element 类型和HIGHLIGHT高亮text格式属于 text format 类型以及工具函数isTableRowDivider和normalizeMarkdown。这说明当前仓库版本的内置能力比 README 示例清单更完整使用前以实际导出的常量为准。5.3 常用打包集合包内置了五组常用打包常量内容TRANSFORMERS全部内置 transformersELEMENT_TRANSFORMERS全部内置 element transformersHEADING、QUOTE、UNORDERED_LIST、ORDERED_LISTMULTILINE_ELEMENT_TRANSFORMERS全部内置多行 element transformersCODETEXT_FORMAT_TRANSFORMERS全部内置 text format transformersTEXT_MATCH_TRANSFORMERS全部内置 text match transformersLINKTRANSFORMERS的组装方式见 MarkdownTransformers.ts按 element → multiline-element → text-format → text-match 的顺序拼接。5.4 顺序即语义内置数组的排列规则阅读源码可以发现两个与顺序强相关的规则源码注释明确写出code 优先TEXT_FORMAT_TRANSFORMERS中INLINE_CODE排在最前MarkdownTransformers.ts因为反引号内的内容不应被其他格式转换防止**等标记在行内代码里被误处理长标记优先BOLD_ITALIC_STAR***排在BOLD_STAR**之前、BOLD_STAR排在ITALIC_STAR*之前保证***text***被识别为粗斜体而不是粗体嵌套斜体。同样的规则也体现在导出端createMarkdownExport会过滤掉多格式 Transformer如***只用单格式 Transformer***分别导出并把包含code格式的 Transformer 排序到末尾避免**Bold Code**这种错误输出MarkdownExport.ts。六、编写自定义 Transformer接口契约与源码级拆解README 提示可查看MarkdownTransformers.js了解实现范例当前仓库对应源码为 packages/lexical-markdown/src/MarkdownTransformers.ts。下面按类型给出接口字段与实现要点。6.1 ElementTransformertype ElementTransformer { type: element; dependencies: KlassLexicalNode[]; // 依赖的节点类用于节点注册 regExp: RegExp; // 匹配行首标记 replace(parentNode, children, match, isImport): boolean | void; export(node, traverseChildren, selection?): string | null; triggerOnEnter?: boolean; // 是否支持回车触发 };export返回null表示放弃导出Lexical 会继续尝试下一个 transformer返回字符串表示该节点由本 transformer 序列化replace返回false表示放弃转换即使正则已匹配isImport参数用于区分是导入操作还是输入快捷键操作——例如HEADING.replace在非导入且父节点为不可替换块QuoteNode时返回falseMarkdownTransformers.ts防止引用块被块级快捷键意外吞掉源码注释引用了 issue #7407块级创建的通用模式是createBlockNodeMarkdownTransformers.ts创建节点 →append(...children)→parentNode.replace(node)非导入时把选区移到新节点开头。6.2 MultilineElementTransformer代码块CODE是唯一的内置多行 transformer比普通 element 多出regExpStart/regExpEnd结束围栏可标记为optional未闭合时匹配到文档末尾以及可选的handleImportAfterStartMatch手工接管导入流程。其实现细节非常丰富值得关注的几点围栏长度自适应导出时若代码内容里出现更长的反引号串会动态加长围栏保证围栏不与内容冲突MarkdownTransformers.ts围栏缩进剥离按 CommonMark 规范起始围栏缩进 N 个空格内容每行最多剥掉 N 个空格stripFenceIndentinfo string 元数据js titlex中语言之后的titlex被存入codeMetaState往返转换不丢失源码注释专门解释了这一设计单行代码块code单行形态也有专门的正则CODE_SINGLE_LINE_REGEX处理导入前的 normalize 阶段识别。6.3 TextFormatTransformer结构最简单只有三个字段type TextFormatTransformer Readonly{ type: text-format; format: readonly TextFormatType[]; // 如 [bold]、[bold, italic] tag: string; // 如 **、*、 intraword?: boolean; // 是否允许出现在单词内部 };intraword的作用ITALIC_UNDERSCORE_和BOLD_UNDERSCORE__设置为false即foo_bar中不会被当成斜体避免与文件名等场景冲突而星号版本ITALIC_STAR未设置该字段行为更宽松。README 指出这些格式最终映射到TextFormatTypebold、italic、underline、strikethrough、code、subscript、superscript内置 transformer 覆盖了其中常用子集。6.4 TextMatchTransformerLINK是唯一的实现范例字段最丰富importRegExp导入时匹配、regExp快捷键匹配、trigger触发字符链接为)、replace与export。链接导出的细节可以体现这个包的严谨程度目的地含空白时改用...尖括号形式空 URL 也走尖括号形式圆括号()、反斜杠、行尾换行等特殊字符分别做转义或转成字符引用#13;/#10;标题支持三种引号拼写双引号 / 单引号 / 圆括号并在往返时保留MarkdownTransformers.ts。七、导入管线从字符串到节点树导入的完整流程入口$convertFromMarkdownString→$importMarkdownNodes见 MarkdownImport.ts大致为按类型索引transformersByType把传入数组按 element / multiline-element / text-format / text-match 分组预处理normalizeMarkdown规范化换行、合并相邻行受shouldPreserveNewLines/shouldMergeAdjacentLines控制并构造 text-format 索引createTextFormatTransformersIndex为每个 tag 生成完整匹配正则单字符 tag 与多字符 tag 的正则策略不同且特意避免使用 Safari 16.4 以下不支持的负向后行断言见 MarkdownImport.ts逐行解析先尝试$importMultiline处理多行元素命中即返回并跳过被消费的行否则走$importBlocks按 element → text-format → text-match 顺序处理单行清理非保留换行模式下移除空段落isEmptyParagraph并把文本节点中的制表符\t拆分为TabNode$normalizeMarkdownTextNode逐个构建节点以避免长制表符串时的调用栈溢出问题。另外导入解析列表时还实现了列对齐感知的嵌套列表通过withListIndentColumns在单次导入过程中跨行记录每个列表层级的内容起始列使1. a的三空格子列表与- a的两空格子列表都能正确嵌套、同列兄弟保持平级MarkdownTransformers.ts这是对 CommonMark 列表规则的忠实实现。八、导出管线从节点树到 Markdown导出端$convertToMarkdownString→createMarkdownExport见 MarkdownExport.ts的关键机制顶层节点依次尝试 element / multiline transformers 的export命中即用其结果否则回退到通用子节点导出DecoratorNode导出其getTextContent()行内格式跨节点闭合exportTextFormat用unclosedTags数组跟踪尚未闭合的格式标记遇到文本兄弟节点时复用打开的标签避免输出**a****b**这种可合并而未合并的碎片同时引入unclosableTags防止链接内部出现**text text**这种把闭合标记关进链接里的非法 MarkdownMarkdownExport.ts空白与 flanking 规则按 CommonMark 要求格式标记必须紧贴非空白字符因此 foo 会导出为**#32;#32;#32;foo#32;#32;#32;**用字符引用保住首尾空白MarkdownExport.ts相邻非空块之间用\n\n分隔空段落渲染为独立换行。九、仓库内可运行的完整示例本仓库自带多个可直接运行参考的 Markdown 集成示例dev-examples/dom-import/src/MarkdownShortcutsExtension.ts在 DOM 导入示例中接入 Markdown 快捷键examples/markdown-editor/src/extensions完整的 Markdown 编辑器示例含多个 extensionexamples/markdown-editor/src/tests配套的转换测试。单元测试方面packages/lexical-markdown/src/tests/unit/LexicalMarkdown.test.ts 与 MarkdownTransformers.test.ts 覆盖了导入导出的往返一致性另有若干针对边角场景的专项测试CodeBlockFenceIndent.test.ts围栏缩进、CodeBlockLeadingBlankLine.test.ts代码块前导空行、EscapedBackslashHardLineBreak.test.ts转义反斜杠与硬换行、Issue5366Repro.test.ts历史 issue 回归。阅读这些测试是快速理解转换行为边界的最佳途径。十、常见实践模式总结需求推荐方案从 Markdown 初始化编辑器initialConfig.editorState () $convertFromMarkdownString(md, TRANSFORMERS)保存时导出整篇 Markdowneditor.update(() $convertToMarkdownString(TRANSFORMERS))复制选中内容为 Markdown$convertSelectionToMarkdownString(TRANSFORMERS, selection)把 Markdown 片段插入光标处$generateNodesFromMarkdownString(md, TRANSFORMERS)selection.insertNodes(...)React 中输入即转换MarkdownShortcutPlugin transformers{TRANSFORMERS} /非 React 输入即转换registerMarkdownShortcuts(editor, TRANSFORMERS)只支持部分语法传自定义 transformers 数组如[HEADING, BOLD_STAR, LINK]保留源码换行各转换函数传shouldPreserveNewLines: true需要提醒的是所有转换 API 都必须在editor.update()回调或editorState.read()等合适的 Lexical 更新上下文内调用以$前缀开头的函数是 Lexical 的内部更新环境专用 APItransformers 中的dependencies数组列出的节点类如HeadingNode、ListNode、CodeNode、LinkNode需要预先在编辑器nodes配置中注册否则导入会失败。总而言之lexical/markdown以显式传入的 transformers 数组为统一抽象把导入、导出、快捷键三条路径串成一套可裁剪、可扩展的机制。理解内置 transformers 的顺序规则、三类接口契约与导入导出管线你就能按需组合出符合业务语法的 Markdown 富文本编辑器。【免费下载链接】lexicalLexical is an extensible text editor framework that provides excellent reliability, accessibility and performance.项目地址: https://gitcode.com/GitHub_Trending/le/lexical创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
上一篇/下一篇内容由系统自动关联
返回资讯列表 →