演进实录:从实验原型到 Markdown/HTML 双向转换的工程实践)
Joplin 富文本编辑器WYSIWYG演进实录从实验原型到 Markdown/HTML 双向转换的工程实践【免费下载链接】joplinJoplin - the privacy-focused note taking app with sync capabilities for Windows, macOS, Linux, Android and iOS.项目地址: https://gitcode.com/GitHub_Trending/jo/joplinJoplin 的富文本WYSIWYG编辑器最早以 v1.0.194 预发布版中的实验性原型登场本篇文章围绕这份发布于 2020 年 3 月的新闻通告readme/news/20200314-001555.md展开梳理该功能的定位、启用方式、已知限制并结合当前仓库的源码编辑器基于 ProseMirror 的实现、HtmlToMd.ts与MarkupToHtml.ts渲染管线、TinyMCE 封装等还原其Markdown 与 HTML 双向转换的核心原理。读完本文你将掌握如何在桌面端启用并评估富文本编辑器、理解为什么部分 Markdown 插件在富文本模式下会丢失格式以及底层转换管线的关键实现位置。一、背景一个呼声最高的实验性功能Joplin 以隐私优先、支持同步的笔记应用定位广为人知其笔记底层一律以 Markdown 存储。在 2020 年 3 月发布的预发布版本 v1.0.194 中官方首次带来一个全新的 WYSIWYG所见即所得编辑器原型。根据原通告的描述WYSIWYG 是当时 Joplin 社区中呼声最高的功能之一它是 GitHub 上点赞数第二高的功能请求同时也是论坛上浏览量最高、评论最多的帖子之一。需要特别强调的是该功能在初始阶段是实验性的。官方在通告中明确警告不要用它编辑重要笔记因为存在内容丢失或被损坏的风险但鼓励用户将其用于不那么重要的笔记以便评估体验并上报 Bug。这一实验性定位为后续整个功能的发展定下了基调——它是迈向正式集成富文本编辑器的第一步。从当前仓库的结构来看这一实验原型如今已经演进为一套完整的编辑器体系桌面端富文本编辑器基于 TinyMCE 封装核心实现在 TinyMCE.tsx编辑器包joplin/editor基于 ProseMirror 构建入口为 createEditor.ts后续版本中Split / WYSIWYG布局模式进一步演进为 Markdown、Split、Rich Text 三种可切换的编辑模式。二、为什么 WYSIWYG 是一个技术难题通告指出WYSIWYG 编辑器在技术上极具挑战性因为它需要在 Markdown 与 HTML 之间进行双向转换MD → HTML把笔记的 Markdown 源文本渲染成可供富文本编辑器编辑的 HTMLHTML → MD用户在富文本编辑器中完成修改后再把 HTML 转回 Markdown 保存。这一转换链路在 Joplin 中分别由两个久经考验的模块承担HTML → MDHtmlToMd类基于 fork 的joplin/turndown与joplin/turndown-plugin-gfm源码见 packages/lib/HtmlToMd.ts。它在 Web Clipper 中得到了大量实战检验。其parse()方法支持一系列可配置选项例如preserveImageTagsWithSize保留带尺寸的图片标签、preserveNestedTables保留嵌套表格、preserveTableStyles保留表格样式、preserveColorStyles保留颜色样式、convertEmbeddedPdfsToLinks把内嵌 PDF 转换为链接、collapseMultipleBlankLines折叠多个空行等同时内置了headingStyle: atx、codeBlockStyle: fenced、bulletListMarker: -、emDelimiter: *、strongDelimiter: **等默认输出规则确保转换出的 Markdown 风格统一。MD → HTMLMarkupToHtml类源码见 packages/renderer/MarkupToHtml.ts。它根据笔记的 MarkupLanguageMarkdown 或 HTML选择对应的渲染器——Markdown 走MdToHtml基于 markdown-it 及其插件体系HTML 走HtmlToHtml随后统一返回包含html与cssStrings的渲染结果。尽管这两条链路本身非常成熟通告仍然提醒Markdown 与 HTML 之间并不存在完美的无损互转可能存在各种作者尚未想到的边界情况edge cases。这一判断在后来的实践中被反复验证也正是下一节所讲部分 Markdown 插件不兼容的根本原因。三、如何启用View → Layout → Split / WYSIWYG在 v1.0.194 中该功能藏得比较深启用方式为进入菜单View → Layout按钮序列选择Split / WYSIWYG点击Layout按钮即可在编辑模式之间切换。Split / WYSIWYG意味着编辑器采用分屏Split视图 WYSIWYG 所见即所得视图的组合分屏视图一侧为 Markdown 源码、一侧为渲染预览而 WYSIWYG 视图则直接对渲染后的富文本内容进行编辑。从当前仓库源码看这一切换逻辑已经演进为更完整的编辑器切换组件。例如 ToggleEditorsButton.tsx 定义了Markdown与RichText两种取值按钮以Markdown 图标 编辑图标双图标形式呈现点击即可在两种编辑器之间切换此外桌面端还支持 Split View分屏视图布局。移动端同样提供富文本编辑能力——在编辑笔记时通过右上角菜单kebab menu选择 Edit as Rich Text 即可详见 readme/apps/rich_text_editor.md。四、已知限制原通告逐条展开通告明确列出首批已知限制理解这些限制对正确使用富文本编辑器至关重要4.1 无法插入插件块KaTeX / Mermaid 需先在分屏视图创建在 WYSIWYG 模式中尚无法直接插入需要特殊语法包裹的插件块如 KaTeX 数学公式、Mermaid 流程图。正确做法是先在分屏Split视图中以 Markdown 语法创建对应的 fenced 代码块切换到 WYSIWYG 视图这些已创建的插件块可以正常显示并被编辑。4.2 部分 Markdown 插件不支持且无法简单修复这是最关键的限制。其根本原因在于转换的不可逆性一旦 Markdown 被转换为 HTML 并在 WYSIWYG 编辑器中显示就无法再转换回原始的 Markdown。具体表现为受支持的插件以围栏fenced代码块形式包裹的插件可以工作例如 KaTeXkatex、Fountainfountain、Mermaid不受支持的插件如 multi-md 表格MultiMarkdown 风格的扩展表格语法。如果在 WYSIWYG 编辑器中打开包含 multi-md 表格的笔记并保存原始的多行 Markdown 表格语法将会丢失退化为普通 Markdown 表格。这一限制的源码级印证来自渲染层与编辑器层的分工。在渲染层packages/renderer/MdToHtml/rules/目录下为各类插件实现了独立的 markdown-it 规则例如 katex.ts支持行内$math$与块级$$...$$公式并集成 mhchem 化学方程式插件、mermaid.ts负责加载 mermaid.min.js 渲染流程图、fountain.ts 等。这些插件在渲染成 HTML 后其内部结构是渲染产物而非原始语法编辑器侧的 HTML→MD 转换只能尽力还原无法保证无损。在后来的正式文档 readme/apps/rich_text_editor.md 中这一限制被概括为一句 TLDR如果你主要打算使用富文本编辑器请避免使用 Markdown 插件并了解编辑器的局限。并进一步细化为大多数 Markdown 插件与富文本编辑器不兼容唯一受支持的是围栏类fenced插件——即用三反引号包裹的插件KaTeX、Mermaid 等可用每个插件的兼容性可以在 Markdown 配置界面查看。4.3 更多由 Markdown 底层格式带来的限制从当前仓库的正式文档readme/apps/rich_text_editor.md看后续版本还沉淀出以下限制均源于笔记底层仍是 Markdown这一设计表格必须有表头这是 Markdown 语法的硬性要求。创建表格时允许不带表头但保存后底层会补上一个空表头下次打开即可见。列表项类型必须统一同一列表中的项必须全为复选框、全为项目符号或全为编号不能混用需要不同类型时应拆成两个列表并用水平线分隔。不支持 vim / emacs 键位模式。Markdown 笔记中的 HTML 可能丢失若笔记为 Markup - Markdown 类型且包含 HTML 片段富文本编辑时这些 HTML 无法被转换为 Markdown可能丢失而 Markup - HTML 类型的笔记不受影响因为不发生此转换。引用链接被内联化保存富文本编辑器改动时所有引用链接[title][link-name]会被转换为内联链接[title](https://example.com)。五、从实验原型到正式功能底层转换管线的演进原通告中的实验原型如今已沉淀为成熟的编辑器体系。理解当前实现能帮你更准确地预判富文本编辑器的行为边界。5.1 渲染管线Markdown → HTML桌面端在加载富文本编辑器时通过MarkupToHtml将笔记的 Markdown 渲染为 HTML。其核心逻辑见 packages/renderer/MarkupToHtml.ts根据markupLanguage选择渲染器——Markdown 语言使用MdToHtml内部是 markdown-it 实例加载各插件规则HTML 语言使用HtmlToHtml。渲染结果包含 HTML 正文与 CSScssStrings后者用于在编辑器中还原与阅读视图一致的样式。5.2 编辑层ProseMirror 与原始标记插件编辑器包joplin/editor基于 ProseMirror 构建。在 createEditor.ts 中可以看到完整的初始化流程通过renderer.renderMarkupToHtml(markup, ...)把 Markdown 渲染为 HTML用ProseMirrorDomParser.fromSchema(schema)将 HTML 解析为 ProseMirror 文档节点树挂载originalMarkupPlugin见 packages/editor/ProseMirror/plugins/originalMarkupPlugin.ts该插件用data-markup装饰decoration把每个顶层节点的原始 Markdown 片段与文档节点一一绑定作为原始标记缓存起来当用户编辑某个节点时仅重新序列化该节点并调用renderNodeToMarkup内部是renderer.renderHtmlToMarkup即 HTML→MD 转换来生成新的标记保存时通过stateToMarkup按顺序把所有节点的标记拼回成完整的 Markdown 文本。这套按节点缓存原始 Markdown 按需重转的机制正是 WYSIWYG 编辑器能够最大限度保留原始 Markdown 结构的关键——未被编辑的节点直接复用原文只有被编辑的部分才经历 HTML→MD 转换从而把无损问题的影响范围控制在用户实际改动过的区域。同一目录下的其他插件则负责编辑体验的细节listPlugin.ts列表、tablePlugin.ts表格、imagePlugin.ts图片资源、detailsPlugin.ts折叠块、searchPlugin.ts搜索高亮、inputRulesPlugin.ts输入规则等。5.3 反序列化HTML → Markdown保存富文本编辑器内容时HTML 会经HtmlToMd.parse()转换回 Markdown。桌面端 TinyMCE 封装中通过props.htmlToMarkdown回调完成这一动作调用点见 TinyMCE.tsx即const contentMd await prop_htmlToMarkdownRef.current( info.contentMarkupLanguage, info.editor.getContent(), info.contentOriginalCss, );HtmlToMd的默认参数atx 标题、fenced 代码块、-列表符、*斜体与**粗体分隔符等保证了转换输出的 Markdown 风格与 Joplin 全局一致。值得一提的是ProseMirror 版编辑器还通过自定义序列化器处理了若干 HTML→MD 的边界细节例如用不换行空格填充空段落避免空段落被删除见 originalMarkupPlugin.ts保留重复空格连续两个空格转成空格 不换行空格代码块内避免使用nbsp;确保pre_block内的文本原样输出。这些细节恰恰印证了通告中存在各种边界情况的预判——双向转换的工程质量正是在一个个边界情况的修复中逐步提升的。六、编辑器的自动格式化Markup Autocompletion演进后的富文本编辑器还内置了 Markdown 风格自动补全详见 readme/apps/rich_text_editor.md 的 Markup autocompletion 一节。在桌面端输入以下模式并按下空格或回车即可自动转为对应格式输入模式转换结果**bold**粗体*italic*斜体highlighted高亮code行内代码$math$行内数学公式KaTeX 语法渲染后可通过双击或右键菜单 edit 再次编辑# Heading 1行首一级标题## Heading 2二级标题### Heading 3三级标题- List项目符号列表1. List编号列表---、___、***水平分隔线注意大多数替换需要在闭合格式化字符后按空格或回车才会触发例如输入test不会立即高亮但在最后一个后按空格即可。若不需要此行为可在 设置 笔记 中关闭 Auto-format Markdown 选项。七、小结与使用建议回到 2020 年 3 月那份通告其核心结论在今天依然成立富文本编辑器是 Joplin 社区高呼声需求但技术根基是Markdown 与 HTML 双向转换而非另起炉灶的私有格式笔记底层永远是 Markdown这带来跨端一致性CLI 客户端没有富文本编辑器也能正常读写也带来不可避免的兼容性限制使用策略若主要使用富文本编辑器应优先使用普通 Markdown 语法标准表格、列表、粗斜体等避免依赖非围栏类的 Markdown 插件KaTeX、Mermaid 等围栏插件可在分屏视图创建后在富文本中继续编辑遇到问题时应把 Bug 上报给官方原通告指向 GitHub issue #176而不是自行修改仓库代码——仓库是只读的仅用于查阅实现与验证行为。从 v1.0.194 的实验原型到如今以 ProseMirror 为核心、TinyMCE 为封装、配合HtmlToMd/MarkupToHtml双转换管线的正式功能富文本编辑器走完了一条先验证想法、再逐步补齐边界情况的演进之路。理解这条链路你就能在 Markdown 与所见即所得之间自由切换而不踩坑。【免费下载链接】joplinJoplin - the privacy-focused note taking app with sync capabilities for Windows, macOS, Linux, Android and iOS.项目地址: https://gitcode.com/GitHub_Trending/jo/joplin创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考