ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

自研一款好看又彪悍的 Markdown 编辑器:从需求梳理到核心实现全复盘

自研一款好看又彪悍的 Markdown 编辑器:从需求梳理到核心实现全复盘 1. 一个重度用户的编辑器执念为什么市面上没有我能全盘接受的工具先交代一下背景。我从2016年前后开始把 Markdown 当成主力书写格式从写博客到记学习笔记再到后来写项目文档、画技术方案几乎所有的文字产出都离不开 Markdown。以我这种用量市面上的编辑器基本都摸过一圈Typora、VS Code、Obsidian、Notion、语雀甚至各种在线编辑器断断续续都试过。每个工具都有自己的强项但问题是——没有一个能让我“全盘接受”。我用 Markdown 编辑器有一些很具体、甚至有些刁钻的习惯。比如我写东西时经常需要来回切换思路一段话可能只有几十个字但我需要它在渲染后看起来舒服我喜欢在写作过程中随时插入数据表格、数学公式、代码块还经常从网页或者 PDF 里复制表格内容进来希望粘贴后能自动整理成 Markdown 表格。然而这些需求在多数编辑器里要么被忽略要么需要额外插件才能解决。最让我崩溃的是图片路径问题。写 Markdown 文档时图片怎么存、路径怎么写、移动文档之后图片还能不能显示这三点几乎成了我长期以来的痛。用 Typora 时图片路径还算可控但一旦切到别的编辑器同一套规则就失效了。这个痛点后来直接决定了我自研编辑器时的功能优先级。我在这件事上琢磨了挺久。最开始也试过在现有编辑器上打补丁比如给 VS Code 配各种扩展或者用脚本做一套自定义工作流。但补丁打得越多越觉得哪里不对劲。编辑器这东西本质上是一个长期相伴的生产力工具如果基础体验不顺手靠堆插件救不回来。与其一直迁就不如自己写一个。于是就有了这个项目一款长得好看、内核也足够能打的 Markdown 编辑器。它的特点用一个词概括——“好看又彪悍”。好看是指界面和排版符合我这套审美体系简约但不简陋彪悍是指功能上能顶得住重度用户的日常操作不拖后腿。这篇文章我会把整个项目从需求梳理、技术选型到核心功能实现、踩坑过程做一个完整复盘希望能给同样在折腾 Markdown 工作流的朋友一些参考。2. 从需求清单到产品骨架我先梳理了六大核心痛点2.1 重度用户真正缺的不是“能编辑”而是“不打断思路”动手写代码之前我先做了一件事把自己过去几年在不同编辑器里遇到的具体问题全部列出来然后按“出现频率”和“打断程度”两个维度打分。这个方法很土但特别有效。因为你不是在替一个假想的用户设计产品你是在替每天都跟文本打交道的自己解决真实痛苦。我列出的六大痛点排序如下痛点出现频率打断程度平时用的工具给的解决方案图片粘贴后路径混乱极高高多数靠手工配置目录表格粘贴过来是乱码高高几乎没有数学公式实时预览困难中高依赖额外预览插件长文档滚动时定位困难中中有的有目录有的没有换行行为不符合预期极高低各编辑器行为不一致导出格式不统一中高依赖第三方工具链这六条看着不多但每一条背后都藏着一堆细碎的衍生需求。比如图片路径问题展开来看就涉及是否自动建目录、是否支持相对路径、是否把图片复制到本地、是否支持从剪贴板直接粘贴截图。表格问题又涉及从 Excel 复制、从网页复制、从 PDF 复制三种来源的数据格式都不太一样。我做了个决策既然要自己开发那功能边界就必须比过去用的工具更宽。不是“能处理标准 Markdown 语法就行”而是“所有非标准场景也要尽量兜住”。这也是这个项目和普通作业级项目最大的区别。2.2 技术方案选型为什么选择 Web 技术栈技术选型我纠结了很久。最后还是选了 Web 技术栈用 Electron 容器 前端框架做界面核心编辑器用 CodeMirror 6 做底层。选择这个组合的原因很现实。第一我是重度用户日常写文档就希望在编辑器里直接完成所有操作渲染层和编辑层要无缝衔接Web 技术栈在这方面生态最成熟。第二Markdown 解析、代码高亮、数学公式渲染这些能力在前端领域已经有大量经过验证的库不需要从零开始造轮子。第三Electron 可以带来跨平台能力我不需要用三套代码分别维护 Windows、macOS 和 Linux 版本。当然 Electron 也有明显的槽点打包体积大、内存吃得多。但作为一个生产工具稳定性、开发效率和生态成熟度比二进制体积更重要。用 Rust 写原生应用的方案我也研究过开发成本高出一大截对我这种单兵作战的项目来说不划算。编辑器底层我用了 CodeMirror 6。它支持模块化扩展输入事件拦截和视图更新都很灵活方便实现“所见即所得”这类高级交互。渲染层配合 Markdown 解析库来做实时预览整体架构分成三层输入层、解析层、渲染层。2.3 界面风格设计的三个准则这个项目的定位是“好看又彪悍”所以在界面设计上我给自己定了三条准则。第一条写字区域要做减法。编辑区不显示任何工具按钮不需要像传统富文本编辑器那样堆一堆图标在顶部。Markdown 语法本身就是格式控制工具我需要的是干净的画布。第二条排版要有纸质感。这听起来有点玄学但实际落地时可以拆成具体指标正文行宽控制在 720px 左右、正文字号 16px、行高 1.75、段间距 1.25em、标题层级用字号和字重区分而不只靠颜色。这些细节直接决定写作时的舒适感。第三条暗色主题不是把背景翻黑就完了。做暗色主题时要重新计算对比度确保语法高亮所有颜色在暗背景下都能清晰辨识代码块的底色要跟正文背景有层次感同时不能刺眼。我前后调了三版才把暗色主题调到一个看着舒服的状态。3. 核心细节解析与实操要点这些功能是怎么一步步实现的3.1 图片处理的完整闭环粘贴、存储、引用、迁移图片问题是 Markdown 编辑器绕不开的坎。我的设计目标很简单用户截图后 CtrlV 直接粘贴编辑器自动把图片保存到本地自动生成文件名自动在文档里写入正确的相对路径。文档挪到任何位置只要图片文件夹跟着走显示就不受任何影响。这个功能的实现路径大致是这样的。监听粘贴事件判断剪贴板里是否有文件类型的数据如果有图片文件就生成一个唯一文件名比如20250218-143200-8f3a2b.png把这个文件写入当前文档同级的assets目录再把![](assets/20250218-143200-8f3a2b.png)插入到光标位置。粘贴处理只是第一步。真正的复杂度在于“文档移动场景”。假设你把 Markdown 文件从一个目录挪到另一个目录图片相对路径需要同步调整。我实现了一个“文档移动检测”机制编辑器会记录当前文档的基准路径在打开文档时检查所有图片引用是否能命中本地文件如果有失效的引用会提示你是否基于新文档位置重新计算路径。这个功能虽然写起来不复杂但确实救了命——我过去被这个问题坑过太多次。还有一个小细节容易被忽略从网页复制图片时有些网页给的是图片的 URL 地址而不是文件本身。这种情况我会先尝试下载图片到本地再插入引用避免文档里挂着一个随时可能失效的外链。如果下载失败才退回保留原 URL 并加上一句注释提醒用户。3.2 表格处理的三种粘贴来源Excel、网页、PDF表格功能是我调研后发现用户需求最旺盛、但几乎没有编辑器做好的方向。热搜词里“markdown表格复制”“markdown表格转换excel”热度都很高说明这确实是高频痛点。我在实现“粘贴转表格”功能时考虑了三种数据来源。最理想的是从 Excel 复制这种格式是 TSV制表符分隔或者表格 HTML非常规整从网页复制会带各种标签需要过滤从 PDF 复制则可能混有换行符和空格最脏可能需要用到行列对齐的智能推断。实现方式是通过一个“智能粘贴识别器”来判断剪贴板内容检查是否包含table标签并决定走 HTML 解析器否则按制表符和换行符分行分列如果既不是 HTML 也没有制表符就尝试用分隔符推断比如多个空格。具体解析为 Markdown 表格后我还会额外做一个对齐优化。Markdown 表格的语法虽然不强依赖列对齐但如果每一行的|竖线位置不对齐源码很难看。所以我写了一个自动对齐函数把每列的内容左右填充到等宽输出后源码整洁漂亮。这个细节虽然不影响渲染效果但对我这种有格式化洁癖的人非常重要。3.3 Markdown 语法解析和自定义扩展的取舍解析器我选了markdown-it主要原因是插件生态好并且可以方便地把解析规则拆解开来自定义。标准 Markdown 语法对我的需求来说只覆盖了约 80%剩下的 20% 需要用扩展补齐。高频扩展有五类。第一个是 GitHub Flavored Markdown 里加删除线和任务列表第二个是数学公式渲染我接入了 LaTeX 引擎支持行内公式和块级公式第三个是表格增强让表格支持单元格换行和块级内容第四个是脚注写长文时确实需要第五个是 Mermaid 流程图在技术文档里用到。这里有个开发上的取舍要不要引入这么多扩展我的答案是默认装上但允许在设置里关闭。因为有些插件可能和第三方渲染工具不兼容用户导出到别的平台时格式可能会乱掉。保留一个开关让用户根据自己的发布平台决定是否启用这比强制绑定更友好。数学公式的渲染性能也值得单独说。初期我用的是公式的即时解析方案每次输入都重新渲染整篇文档里所有公式。当文档超过 50 个公式时明显感到卡顿。后来我改成“懒渲染”只在包含公式的块进入可视区域时才渲染滚动离开后保留缓存结果。优化之后性能提升了 80% 以上。3.4 换行行为和常见语法细节的处理策略“Markdown 换行”这个热搜词我一直很在意。不同编辑器对换行的处理确实不一样Typora 采用所见即所得直接回车就是段内换行双回车是段落分隔而 CommonMark 标准里行尾加两个空格才是段内软换行。我的方案是做成可配置的默认兼容 CommonMark 规范但在工具里提供“宽松模式”开关。开启后单个回车会被渲染成br适合写博客草稿或者聊天记录整理关闭后则严格按规范解析。这个设计既能照顾标准派也能照顾实用派。这里有一个必须提醒的坑换行配置如果写进文档内容就会破坏跨工具兼容性。比如你在宽松模式下写的文档拿到其他编辑器里所有单回车全部变成段落分隔排版会乱套。所以我建议把换行模式设置绑定到“当前编辑器实例”而不是写入文档源文件。这样文档本身始终是纯净的 Markdown。3.5 长文档的导航骨架大纲、滚动同步、文档定位写够一万字以上时编辑器必须解决导航问题。我实现了一个侧边大纲栏滚动文档时自动高亮当前章节。这个功能不复杂但有一个很有意思的细节大标题和普通文本混合在一起时大纲结构可能出现“断层”。比如你在一个##后面直接跟一个####此时###的层级是缺失的如果大纲按树状展示子标题就会跳级嵌套视觉上很突兀。我选择在大纲栏里“智能拼线”如果检测到层级跳级就自动插入一个虚位节点表示这里有一个结构缺口但不会强改用户的文档结构。另外大纲栏可以折叠子层级支持按标题级别过滤显示还会显示各级标题的文字数量作为写作进度的辅助信息。长文档带来的另一个痛点是光标附近内容丢失上下文。目前的方案是在滚动条旁边绘制一个小型的“文档结构热力图”用色块表示文档各段的长度密度一眼就能看到长章节在哪里、文档的整体结构是否失衡。这些细节都是普通函数式 Markdown 编辑器不会管的但恰恰是写作体验的关键所在。4. 实操过程与核心环节实现从零到可以日常使用的完整流程4.1 项目初始化与工程结构布局这个项目的目录结构我按模块规划成这几块editor-core/ # 编辑器核心逻辑与 UI 无关 parser/ # Markdown 解析器封装 plugins/ # 各种 Markdown 扩展插件 commands/ # 编辑器命令插入、格式化、粘贴处理等 renderer/ # Electron 渲染层 components/ # 界面组件 themes/ # 主题定义 utils/ # 工具函数 main/ # Electron 主进程 fileManager/ # 文件读写、目录监听 clipboard/ # 剪贴板处理主进程侧 export/ # 导出功能模块化拆分带来的最直接好处是“编辑器核心”和“界面层”可以独立测试。也就是说后续如果需要做命令行版本、或者把核心能力移植到 Web 端直接对核心层做包装就行不需要重写解析逻辑。工程配置上我用了 TypeScript 严格模式。前端项目一旦到了几千行没有类型系统基本上靠手感硬扛迟早出事。严格模式短期看会拖慢一点开发速度但省下的调试时间远比多写几个类型标注多得多。4.2 核心代码实现一个迷你版的粘贴转表格函数粘贴转表格这个功能我打算把核心代码结构拆开聊聊不是贴完整代码而是把关键判断逻辑说清楚。第一步是判断剪贴板内容类型。比如用户从 Excel 复制过来的一片区域数据会以 HTML 表格和纯文本两种形式同时出现在剪贴板里。我优先读纯文本格式因为纯文本相对容易解析而且不会混入样式属性。第二步是猜测表格的分隔性质。TSV 数据的特点是包含\t制表符并且换行符\n作为行分隔符。如果同时满足这两个条件基本可以确定是从 Excel 或者 CSV 编辑器复制出来的。第三步是解析。按行拆分再按制表符拆列得到二维数组。然后清洗去掉空行去掉全空列。最后把二维数组渲染成 Markdown 表格字符串。第四步做对齐优化。算出每一列的最大宽度遍历所有行对每个单元格做两侧 padding让源码对齐。再组装成最终的 Markdown 表格。HTML 来源的表格处理会稍微复杂一点。我会先用一个轻量的 DOM 解析器提取出行和列然后逐格清理文本内容去掉内联样式、打断长句的空白字符再走同一个二维数组渲染管道。这样表格粘贴功能实际上是一套“多来源 → 标准化 → Markdown 渲染”的流水线后期如果再遇到新的来源格式只需要在入口处加一个新的解析器就行。4.3 实时预览的双缓冲渲染策略实时预览是编辑器的门面功能。我设计的预览方案不是一边打字一边全量重渲染而是采用“双缓冲渲染”左侧为源码编辑区右侧为预览区每次输入变化后只更新变化部分的 DOM 节点而不是把整个预览区清空重绘。实现上我用了 MutationObserver 监听编辑区的行内容变化对比后生成增量补丁更新预览区对应段落。这样即使一个文档有几千行输入响应也不会卡顿。有些用户喜欢“单栏模式”也就是 Typora 那种纯渲染模式文字本身就可以直接编辑。这个模式我做了一版但优先级排得靠后因为纯渲染模式要处理的问题更多输入的焦点定位、实时光标同步、代码块的坐标计算每一项都比双栏模式复杂。对于第一版来说双栏的“所见即所得”程度已经足够日常使用了。另外我还在预览区做了“定位同步”光标在源码区停留在哪一行预览区就高亮对应段落的背景色点击预览区段落也能反向跳转到源码对应位置。这个功能用到了编辑器底层的位置映射——把源码行号映射到渲染 DOM 结构实现起来需要维护一个索引表。这个索引表在文档结构变化时要做增量更新里面有不少边界情况要处理。4.4 导出 PDF 和 Word一条和平时的 Markdown 转换思路不太一样的路线导出功能是绕不开的。Markdown 的发布渠道很多博客平台、公众号、PDF、Word每个目标格式的处理方式都不一样。PDF 导出我走了“HTML 打印”路线。编辑器内置一个渲染 HTML 模板把 Markdown 渲染好的内容嵌入套上纸张尺寸和页边距的样式用 Electron 的打印功能直接生成 PDF。这样做的最大优势是样式完全可控代码高亮、表格边框、公式渲染的效果都能原样保留不会出现用 pandoc 转换时样式丢失的问题。Word 的导出则复杂一些。我先是将文档解析为 HTML再用 HTML 转 DOCX 的库将分块元素映射成 Word 对应元素。标题映射成 Word 标题样式这样 Word 里可以自动生成目录表格映射成真正的 Word 表格而不是截图公式则映射成 OMML 格式——Office 的数学公式格式。这条路线做下来Word 文件的编辑体验比预想中好很多“markdown 转 word 工作流”这个需求我可以说是真正稳落地了。4.5 实测用编辑器写完一篇长文过程中的真实记录开发完成第一版后我干了一件事来验证这个编辑器“配不配当主力工具”用它写一篇一万多字的开源项目使用文档。文章包含大量代码块、四五个数据表格、接近二十个数学公式还配了十几张本地截图图片。整篇写完过程记录如下。图片粘贴环节从截图到写入文档基本无感快捷键操作后立刻就能在源码区看到图片引用生成表格粘贴环节我从 Excel 复制了一份包含三十多行数据的项目费用表粘贴后直接在预览区看到规整的表格但 PDF 复制的那份表格有一点噪音出现了一个跨行合并的单元格结构不能完美还原但数据基本没丢数学公式输入时输入行内公式的语法后不到一瞬就能看到 Live 渲染结果双栏模式下体验很流畅没有明显延迟。整体体会是之前因为图片和表格问题中途换工具的情况这款编辑器没有再出现。当然也有几个功能明显还比较粗糙比如跨行单元格合并的表格还原、更复杂的 Mermaid 节点交互、目录跨页跳转等这些自然还是需要迭代的地方。5. 常见问题与排查技巧实录5.1 图片在文档移动后全部丢失新的编辑器读取文档时我会检查所有图片引用的路径。如果检测到相对路径对应的文件不存在会弹出一个“路径重定位”提示。用户只需要选择新的图片根目录编辑器就会自动重写所有图片引用路径。这个功能一定要做成批量操作不要一个个图片去改否则移动一个包含 30 张图片的文档真的会改到崩溃。5.2 粘贴表格后列数对不齐这个问题的根因一般是单元格内容里带有制表符或者看似空格实际上是全角空格。我的处理是粘贴前会做一次“隐形字符清理”——把全角空格转成普通空格去掉单元格文本内多余的换行。如果清理完了列数仍然不一致会把缺列的行标为“脏行”允许用户手动修正行列而不是在解析阶段就把数据弄丢。5.3 数学公式在预览时不刷新如果出现公式改了但预览不更新的问题通常是因为解析器的缓存没有按引用关系正确失效。我的做法是给每个公式块加一个内容 hash内容变化时强制重新解析渲染并强制刷新关联的预览节点。排查时可以先看文档数的块数量有没有变化再用主题切换强制刷新整个渲染层来确认是不是缓存问题。5.4 大文档滚动时预览区卡顿长文档滚动卡顿基本都是“渲染任务过于密集”导致的。我的优化策略是滚动过程中把预览区的更新间隔限制到 100ms 内只触发一次当前不可见的区域渲染为占位容器等滚动停止后再填充内容。这样即使文档非常长滚动也会很跟手。5.5 语法高亮与代码块显示异常代码块高亮偶尔出现异常大多数时候是语言标识没有正常映射到高亮器支持的语言列表。我在高亮器外层做了一层“语言别名归一化”把js、javascript、node统一映射到同一个高亮规则避免出现未知语言时直接返回纯文本。同时设置默认的未知语言处理策略——按纯文本处理但保留颜色区分。5.6 快捷键失灵的可能原因快捷键失灵通常不是因为事件绑定失效而是焦点落到了其他区域。尤其是输入法处于中文模式时按键事件会被输入法拦截。我的建议是把快捷键系统分两层编辑器内部快捷键一律监听keydown事件并排除输入法组合键全局快捷键交给主进程处理再发消息通知渲染层。这样跨平台的表现会更稳定。6. 踩过的坑、验证过的结论、还有一些掏心窝的建议自研编辑器这条路我走了差不多半年从第一行代码到现在用它写完整篇文档中间踩过很多坑也积攒了一些经验。现在回头总结有五条建议值得分享给同样想折腾工具的开发者。第一条就是不要一上来就追求“功能全面”要把“核心流畅”放在第一位。我第一版只做了编辑区、预览区、文件打开保存以及图片粘贴这四项基本功能但每一项都做得足够顺手后面功能越来越多时核心体验始终没有垮掉。用这种增量式的开发节奏每次迭代都有能拿来用的东西心理上也更有成就感。第二条Markdown 解析器不要自己从头写。这绝对是“重复造轮子”的重灾区。markdown-it等成熟的解析器已经处理掉了大量边界情况直接在上面做插件扩展是最省力、也最不容易出错的方式。自己解析 Markdown 看起来很酷但当你发现需要处理嵌套列表、代码块中的特殊字符、表格单元格里的竖线转义时就会明白这个决策有多正确。第三条数据安全永远要在功能开发前面。编辑器的核心价值是“不让用户丢内容”。我做了自动保存默认每 30 秒保存一次草稿副本文档未保存就关闭时会先弹窗确认然后自动备份一份临时文件。这些机制刚开始用会觉得多余但一旦赶上电脑意外断电或崩溃恢复你会感激这些设计。第四条主题定制要趁早做。编辑器的“好看”是非常主观的感受与其花时间说服不同审美的人喜欢你的默认主题不如把主题系统做好让用户可以自己调整编辑区背景、代码块配色、字体、行宽、行高等。主题系统做成可配置的用户留存率会明显提升。最后一条做给“自己用”的工具才有机会做成好工具。不和实际需求结合的设计最终都会变成花架子。恰恰是“我自己就是目标用户”这种状态能让你真正理解每个功能是否顺手、每个细节是否到位。这个编辑器目前的状态已经基本覆盖我日常写作的完整闭环——从新建文档、粘贴图片、插入表格、书写公式到导出 PDF 和 Word一路顺畅。后续我的想法是补上更好的 Mermaid 交互、更智能的大纲组织以及脚本插件系统。如果你也在折腾 Markdown 工作流欢迎自己动手写一个“趁手兵器”。工具这东西别人给的只能用自己做出来的才叫顺手。
返回列表