说起 Markdown,我和它的关系大概比大多数同事和朋友都要深一些。日常的笔记、技术文档、博客草稿、项目 Readme、甚至会议纪要,我几乎全部用 Markdown 完成。每天早上打开电脑,第一个碰到的工具就是编辑器,晚上合上电脑前最后关掉的也是它。但就是这样一个每天相处超过八小时的工具,我在很长一段时间里都处于一种"勉强能用,但总觉得哪里别扭"的状态:有的编辑器界面好看但功能太弱,有的功能齐全但丑得让人不想打开,有的既好看又强大却偏偏要联网或者收费订阅。用了一圈下来,我决定干脆自己动手,开发一款既好看又彪悍的 Markdown 编辑器。这篇文章就把我的开发动机、核心功能设计、技术选型思路,以及实际开发中踩过的那些坑完整记录下来,给同样折腾过 Markdown 工具的朋友一个参考。
我自己对这款编辑器的定位很明确:它不是一个"玩具",而是能真正扛住重度日常使用的生产力工具。所谓"好看",不是指皮肤多、动效炫,而是指排版舒服、视觉层次清晰、长时间盯屏幕不累;所谓"彪悍",是指 Markdown 里那些让人头疼的痛点——表格编辑、图片路径管理、数学公式、导出 PDF、大文档性能——都要有让人满意的解法。这篇文章既是我的项目复盘,也是一个面向同类需求的完整参考。
1. 为什么要自己动手:市面编辑器解决不了的问题
1.1 我和 Markdown 的日常:一天八小时的使用场景
先说一下我实际的使用场景。白天上班,我会用 Markdown 写技术方案、接口文档、复盘记录,这部分大概占两到三小时。晚上和周末,我会把自己的一些想法整理成博客文章,或者维护开源项目的文档,这部分工作几乎全部依赖 Markdown。算下来,我每天在 Markdown 文件上花的时间确实超过了八小时。
在这样高频的使用下,我对编辑器的要求会变得很具体。比如写文章时经常需要调整段落结构,那么大纲视图和快捷折叠就很重要;写技术方案时经常要贴代码块,那么代码高亮和复制按钮就是刚需;写文档时经常要插入截图,那么图片的处理方式直接决定效率。这些需求单看都不难,但叠加在一起,市面上能同时满足的编辑器就很少了。
还有一个容易被忽视的点:工作流的一致性。我白天在公司的 Windows 上写文档,晚上在家里的 Mac 上写博客,周末可能在 Linux 服务器上临时改个文件。如果编辑器在不同系统上的表现不一致,或者配置文件不互通,体验就会大打折扣。这也是我考虑自己开发的一个重要原因——我想要一套完全由自己掌控的、跨平台体验统一的工具。
1.2 现有编辑器的三个普遍痛点:表格、图片路径、混合内容
用过的 Markdown 编辑器不少,从简单的文本编辑器加插件,到功能全面的商业化软件,各有各的短板。我归纳下来,最影响体验的痛点集中在这三个。
第一个痛点是表格编辑。Markdown 的表格语法本身就很反人类,尤其是列数多、单元格内容长的时候。在纯文本里对齐竖线简直是一场噩梦,稍微改一个字,整个表格的行宽就乱了。虽然有些编辑器提供了表格可视化编辑,但操作起来仍然笨重,不够直接。
第二个痛点是图片路径管理。写博客时,图片通常要放在项目目录下,而写笔记时,图片可能要复制到笔记本的附件目录。不同的编辑器对图片的处理方式完全不同,有的只支持粘贴到默认目录,有的需要手动填路径,有的会自动上传到图床但配置复杂。我经常遇到的情况是:在这台机器上写的文档,换到另一台机器上,图片路径全变了,整个文档的配图全部挂掉。
第三个痛点是混合内容排版。这里的混合内容指的是文字、代码块、数学公式、流程图、表格混在同一篇文档里的情况。很多编辑器在某些单项上做得不错,但一旦混合起来就容易出问题:代码块里的特殊字符被转义了、数学公式渲染和代码高亮冲突、表格里塞代码块直接崩溃。真正重度使用的人,一定会遇到这些组合场景,而这些恰好是大多数编辑器测试覆盖最少的部分。
1.3 我定义的"好用"标准:三项基本原则
经过长期使用和对比,我给"好用"定下了三条基本原则。这三条原则后来直接指导了我自己的编辑器设计。
第一,输入不能有割裂感。写 Markdown 的时候,我的思维应该集中在内容上,而不是被工具打断。这意味着常用操作要尽可能少的快捷键,要支持流畅的自动补全,要有响应迅速的实时反馈。
第二,所见必须接近所得。虽然不是严格的 WYSIWYG(所见即所得),但预览效果应该和最终发布效果高度一致。最典型的情况是:写博客的人用编辑器预览是一回事,发布到网站上看到的又是另一回事。如果两者的排版差距太大,写的时候就会心里没底。所以我要求编辑器的预览渲染结果,能贴近主流的静态博客主题风格。
第三,文档应该是长期资产。这意味着编辑器生成的所有文件都应该是标准 Markdown 格式,图片资源管理方式要清晰可控,不能依赖某个编辑器特有的私有格式来保存数据。万一哪天编辑器不再维护了,我的所有文档还能用任何工具正常打开、正常编辑。
2. 好看从哪里来:编辑器外观设计的三个层次
2.1 第一层:编辑区排版本身的质量
很多人觉得编辑器好看就是皮肤好看,换个字体换个配色就完事了。但真正的排版质量,藏在一堆不容易察觉的细节里。
首先是字体选择。中英文混排是 Markdown 文档最常见的场景,但很多编辑器的中英文字体搭配非常随意,中文用默认的宋体或者黑体,英文用系统默认字体,混在一起时基线不齐,视觉上非常难受。我在这款编辑器里做了字体回退链和基线对齐优化,中文使用思源黑体或者苹方,英文用 Inter 或者 Source Sans Pro,代码用 JetBrains Mono。三者之间有协调的字重和行高,长时间阅读眼睛不容易疲劳。
其次是行距和段距。Markdown 的段落之间天然有间距,但很多编辑器把行距、段距处理得过于紧凑或用太松。我边开发边测试,参考了主流阅读类应用的行高比例,最终把行距控制在字号的 1.7 倍左右、段距在行距的 1.2 到 1.4 倍之间,并且对标题、列表、引用块分别设置了独立的间距参数。栏目页和预览页的差异尽量缩小,这样切换时不会有明显的"变形感"。
最后是排版细节的精细化。比如中英文之间自动加薄空格、标点符号的挤压、代码块的背景色与正文字号的比例、引用块的左边框颜色和宽度。这些细节单看都不起眼,但组合在一起就是决定编辑器是否"耐看"的关键差异。
2.2 第二层:主题系统与沉浸模式
界面外观的第二个层次是主题系统。市面上很多编辑器的主题只是换个背景色和文字颜色,但真正好的主题应该能区分功能区域的视觉层级,并根据使用环境自动调整。
我的编辑器内置了亮色、暗色、护眼三套主题。亮色主题使用浅灰背景,而不是纯白,长时间看能轻微减少眩光感;暗色主题不是简单把背景变黑,而是采用深蓝灰色调,配合降低饱和度的语法高亮,避免在暗色环境下产生刺眼对比;护眼主题给编辑区和预览区同时加了微暖的底色,适合晚上长时间写作的场景。
除了静态主题,我还加入了一个很多人觉得没必要、但我觉得非常实用的功能:沉浸模式。在这个模式下,编辑区以外的所有 UI 元素自动隐藏,页面居中,当前段落高亮,其他段落轻微半透明。写初稿的时候,视线可以完全聚焦在当前要表达的内容上,不会被侧边栏目录、文件树、状态栏干扰。这个模式我实际用下来效率提升非常明显,尤其是写长文的时候。
2.3 第三层:细节动效与质感
第三个层次是动效和质感。我平时对软件里的花哨动效是比较反感的,但如果动效用得好,它能起到很实际的辅助作用,而不是纯装饰。
我在编辑器里做的动效都很克制,核心就三处:一是光标所在行有一个极淡的背景高亮,过渡动画持续时间在 150 毫秒左右,不抢注意力,但能帮你在多窗格布局里快速定位到当前编辑位置;二是折叠区域展开和收起时有一个平滑的高度过渡,这对阅读大纲时理解文档结构帮助很大;三是打开文件时页面内容有一个轻微的淡入效果,配合预渲染可以避免白屏闪烁。
构建质感还有一个容易被忽略的点:窗口布局的拖拽体验。三栏布局(文件树、编辑区、预览区)的分隔条宽度和拖拽响应速度,直接决定了"这个软件高级不高级"的第一印象。很多编辑器分隔条只有 2 像素宽,鼠标稍微偏一点就拖不动。我在开发时把分隔条调整到 5 像素的热区范围,鼠标靠近时高亮提示,拖拽时内容区域实时重排而不是等松手才刷新,这个小改动让整个软件的手感提升了一大截。
3. 彪悍体现在哪里:核心功能设计与实现
3.1 实时预览与 Markdown 解析引擎的设计
实时预览是 Markdown 编辑器的核心功能,但实现方式直接决定了编辑体验。市面上的编辑器主要分两派:一派是"滚动同步",编辑区和预览区是两个独立区域,输入时通过监听滚动事件保持位置同步;另一派是"即时渲染",编辑区本身就是渲染效果,语法标记隐藏,看到的就是渲染后的样子。这两派各有优劣,我最初选了滚动同步方案,原因很实际:它对输入法的支持最稳定,光标定位最可靠,不会有光标漂移的问题。
实时预览的关键在于解析引擎。我没有直接用现成的marked或markdown-it一把梭,而是在markdown-it的基础上做了二次开发。为什么选它?因为markdown-it的插件机制非常成熟,而且它支持自定义渲染规则。我从一开始就知道会遇到数学公式、流程图、任务列表、脚注这些扩展语法,用插件化的结构来组织这些扩展,后续维护成本会低很多。
解析引擎的性能是另一个硬指标。我处理的文档经常有几万字,加上大表格和长代码块,如果一输入就全量解析,很容易卡顿。我采用的方案是分段解析:把文档按标题层级切分成块,每次编辑只重新解析被影响的那个块,而不是整个文档。同时配合去抖策略,输入过程中的中间状态不立即渲染,等输入暂停 80 毫秒以后再统一渲染。这样在长文档里的输入流畅度,能够接近纯文本编辑器的水平。
3.2 表格编辑:把 Markdown 最痛苦的部分变成可视化操作
表格是 Markdown 语法里的重灾区。我在这款编辑器里花了不少精力在表格功能上,目标只有一个:让表格编辑像 Word 一样直观,但同时保留 Markdown 的源码可控性。
我的方案是,在表格所在的段落范围内提供一个独立的类似电子表格的编辑浮层。当光标聚焦在表格区域时,预览区会显示一个可编辑的可视化表格,点击单元格可以直接输入内容,支持按键切换单元格、回车换行、Tab 跳格。编辑完成后,浮层会自动反算 Markdown 源码,并把对齐用的空格重新排好。
这个功能的实现难点在于解析和序列化的双向转换。先把表格的 Markdown 源码解析成二维数组,把合并单元格、对齐方式也一并解析出来;编辑时维护这个二维数组的状态;编辑完成后,再把数组序列化成对齐规整的 Markdown 表格源码。这里有个细节:很多人手动写表格时会不舍得在空单元格里加占位空格,导致表格渲染错位。我的序列化逻辑会在空单元格里自动补 和空格,保证任何情况下渲染都不乱。
我还在表格里加了一个非常实用的功能:从 Excel 复制数据直接粘贴成 Markdown 表格。以前我从 Excel 复制一块数据,粘贴到 Markdown 编辑器里,大概率只是一堆用 Tab 分隔的纯文本。现在我的解析器会在粘贴时识别剪贴板里的表格数据格式,自动转换成标准的 Markdown 表格语法。反过来,我也可以一键把 Markdown 表格复制成 Excel 能识别的 TSV 格式。网上有人专门搜索"markdown表格转换excel"、"markdown表格复制",说明这个需求其实非常普遍。我做了这两个方向的转换,实测下来大家反馈最多的就是表格相关的功能最实用。
3.3 图片路径管理与粘贴上传
图片路径问题是 Markdown 使用中最让人头疼的问题之一,也是我自己开发时优先要解决的痛点。我的方案分为三层。
第一层是运行时路径解析。编辑器在渲染文档时,会智能解析图片的相对路径和绝对路径。如果图片路径相对于当前 Markdown 文件存在,就直接使用;如果相对于项目根目录存在,也能正确解析;如果磁盘上的实际路径和文档中写入的路径不一致,编辑器会在不影响源码的前提下,在预览时自动匹配最近的真实路径。这样即使文档是从别处拷贝过来的、图片路径已经变了,预览也总能尽量显示正确的图片。
第二层是粘贴时的自动处理。在编辑器里直接粘贴截图时,可以选择把图片保存到与当前文档同级的assets目录、当前项目指定的图片目录,或者统一上传到配置好的图床。保存时会自动生成时间戳加随机字符的文件名,避免重名覆盖。如果保存成功,编辑器会自动在光标位置插入对应的 Markdown 图片语法,并且写入相对路径。
第三层是路径失效检测。图片无法加载时,编辑器会在预览区给出明确的占位提示,显示目标路径,并提供一个"定位文件"按钮,让用户可以手动重新关联图片。这个功能在网上搜索"markdown图片路径"的问题帖里出现频率很高,但很多编辑器都没有提供足够友好的处理机制,导致用户经常要手动打开资源管理器去一张张检查图片路径。
3.4 数学公式与代码块:扩展语法的集成方式
Markdown 的原始语法非常简单,真正让它在技术圈流行的,是社区扩展出来的各种语法:数学公式、流程图、时序图、任务列表、脚注、划线等等。一个"彪悍"的 Markdown 编辑器,必须把这些扩展语法一并处理好。
数学公式这块,我集成的是 KaTeX 而不是 MathJax。原因很实际:KaTeX 的渲染速度比 MathJax 快非常多,大概有数量级的差距。写公式密度大的数学文档时,MathJax 很容易在快速滚动或者编辑时产生明显的渲染延迟,但 KaTeX 几乎能做到瞬时渲染。虽然 KaTeX 的语法兼容性不如 MathJax 覆盖得广,比如某些 LaTeX 的高级宏不支持,但对日常写数学文档来说,KaTeX 的覆盖范围已经足够。我在插件里做了兼容层,把常见的\begin{aligned}、\over、\dfrac等语法做了转换处理,实测下来大部分数学系的内容都能正常渲染。
代码块的处理上,我做了几个差异化功能。一是语言自动识别,粘贴代码时可以自动检测语言类型并高亮;二是代码折叠,长代码块块级可以折叠成一个标题行,方便快速浏览;三是复制按钮,预览区代码块右上角悬停时会出现复制按钮,一键复制代码内容;四是行号显示,这个对技术文档尤其友好,可以精确引用某一行代码。
流程图这块我支持了 Mermaid 语法。但这里有个需要注意的事情:Mermaid 本身有大量的渲染模式和配置项,直接集成会让包体积变大,加载变慢。我的方案是采用懒加载,只有当文档里检测到 Mermaid 代码块时才动态加载渲染引擎,平时不占用资源。这也符合我"不为了做一个功能而拖累整体性能"的原则。
3.5 导出能力:PDF、HTML、Word 三种输出路径
Markdown 的价值不仅在于编辑体验,更在于它能方便地转换成其他格式分享。我的编辑器在设计时就把导出功能作为核心能力来打造。
导出 PDF 是我花时间最多的一项。常见的方案是直接用浏览器打印功能把 HTML 转 PDF,但这样出来的 PDF 分页非常傻,经常在代码块中间或者表格中间断页,也很容易被浏览器打印样式的默认 margin 影响。我的方案是:先将 Markdown 渲染成一套独立的打印优化 HTML,再借助无头浏览器按自定义分页规则排版输出。这里的核心是 CSS 分页规则——我针对标题、表格、代码块分别设置了page-break-before和page-break-inside: avoid,确保表格和代码块不会在中间被切断。同时支持自定义页眉页脚、页边距、纸张尺寸。
导出 HTML 相对简单,相当于把渲染后的内容加上一套内置的样式表打包输出。但我在这个功能里做了一个额外的设计:导出的 HTML 是自包含的,图片会被转成 Base64 嵌入 HTML,这样即使对方没有网络也能正常打开看到全部内容。这个在处理会议纪要、分享给外部合作方时非常方便。
导出 Word 则是通过 Pandoc 来做的。老实说,这个功能我一开始觉得可有可无,但后来发现实际需求很大。很多合作方那边还是以 Word 文档为标准交付格式,所以我在工具内部集成了 Pandoc 的调用逻辑,一键将 Markdown 转成带基础样式的 docx 文件。网上现在也有很多人用 Coze 这类工具搭建"markdown转word工作流",说明需求确实存在。我在实现时做了两级策略:如果本地安装了 Pandoc,就直接调用以获得最好的转换效果;如果没有安装,则退回到一个基于 HTML 转 Word 的兼容方案,效果稍差但能保证输出可用。
4. 技术选型与架构:从零开发一个编辑器要用什么
4.1 技术栈的选择:Electron + React + TypeScript
在选型阶段,我认真考虑过原生桌面技术栈和跨平台框架的取舍。我的核心需求是跨平台——我日常在 Windows、macOS、Linux 三个系统之间切换使用,如果为每个平台开发原生应用,维护成本会成倍增长。所以跨平台框架是必然选择。
最后我敲定的技术栈是Electron + React + TypeScript。Electron 虽然常被诟病内存占用大,但它的成熟度和生态无可替代,而且在做导出 PDF 这类需要无头浏览器的功能时,Electron 内置的 Chromium 可以直接复用,省去了很多打包分发的工作。React 负责 UI 层,组件化的结构对编辑器的功能组织非常有帮助。TypeScript 则保证了这个项目在功能越来越多的情况下,代码仍然可控、可维护。
编辑器内核我使用了CodeMirror 6。选择它的原因是它在处理中英文混排、输入法组合态、大文档性能这些方面有着非常扎实的积累。CodeMirror 6 的设计很现代,底层用纯函数式的 state 管理,支持事务化地更新文档,这对实现复杂的文档操作(比如全局替换、格式化、批量图片路径修正)非常友好。
4.2 编辑器的三大部分:编辑器内核、渲染管线、文件系统
整个编辑器的架构可以粗略分为三大部分,它们各司其职:编辑器内核负责文本输入与编辑操作,渲染管线负责将 Markdown 源码转换为可视化界面,文件系统层负责与磁盘上的文件打交道。
编辑器内核基于 CodeMirror 6,主要处理光标管理、选区、自动补全、代码折叠、快捷键映射这些基础能力。渲染管线则是通过前面说过的分段解析引擎,把 Markdown 源码解析为 AST,再转为 React 组件树渲染成预览区。文件系统层负责文件的打开保存、目录树的读取监听、文件变动检测——如果文件在外部被修改,编辑器会提示用户重新加载,避免覆盖掉别处做出的修改。
这三部分之间的通信协议是这条架构的关键。我定义了一套统一的消息总线,所有跨层的操作都通过消息总线传递。比如用户在预览区的表格里编辑了一个单元格,这个操作会生成一个"表格更新事务",通过消息总线传递给编辑器内核,内核再以事务的方式更新文档源码。这样设计的最大好处是:所有操作都是可撤销的,并且撤销历史能追踪到预览区的可视化编辑操作。这一点很多编辑器做不到,它们往往只是同步了视觉,却破坏了撤销栈。
4.3 性能优化:大文档不卡的关键手段
性能是重度用户最敏感的指标之一。一个几万字的文档,如果在输入时明显掉帧,或者打开时要卡顿几秒,那么再好看的功能也会大打折扣。我在开发中花了大量时间做性能优化,这里分享几个最有效的关键手段。
第一个是虚拟滚动。预览区不是一次性把所有渲染出来的 DOM 节点都挂载上去,而是只渲染可视区域和上下缓冲区域的节点。几万字的文档,如果全部渲染,DOM 节点数量会轻松破万,浏览器滚动起来就是一个字:卡。用虚拟滚动后,任意时刻实际的 DOM 节点只有几十个,滚动的流畅度和纯文本页面几乎没有区别。
第二个是增量解析与缓存。我前文提到文档被切成块,每次编辑只重新解析受影响的块。但还要配合一层缓存:解析结果 AST 会按块缓存,滚动到某个区域时直接取缓存,不需要重新解析。只有真正发生编辑的块才会触发重新解析,并且结果会同步更新缓存。
第三个是渲染降级策略。当检测到当前设备的 CPU 或内存接近阈值时(比如同时打开了多个大型文档),编辑器会自动关闭一些非关键的视觉效果,比如代码高亮的精细化模式、主题的动态过渡动画,甚至自动降低预览的刷新率。虽然画面表现稍微降级,但保证了输入和滚动不卡顿。对我来说,"能一直正常工作"比"偶尔飞快但经常卡死"要重要得多。
5. 开发过程中最容易踩的坑:实测排错记录
5.1 光标位置丢失:CodeMirror 事务合并的边界
开发编辑器的过程中,第一个让我印象深刻的坑是光标位置丢失。具体表现是:在预览区做了某个可视化操作之后,切回编辑区,光标突然跳到了文档开头,或者根本不在原来的位置。
排查过程是这样的:我最初以为问题出在焦点管理上,以为是预览区获取焦点后,编辑区失去了光标上下文。但检查发现焦点一直保持在编辑区,真正的原因是预览区发起的文档更新事务,没有带上正确的光标位置信息。CodeMirror 6 的文档更新是通过事务来完成的,事务可以指定selection字段,但如果这个字段缺失或者指定了一个已经被消除的旧位置,编辑器就会回退到默认位置(通常是文档开头)。
找到根因后,我的修复方案是:在所有由预览区发起的事务里,显式携带一个基于事务前后文档变化的"位置映射对象"。CodeMirror 6 提供了一个非常强大的changes.mapPos()方法,可以把旧文档中的位置映射到新文档中的正确位置。我统一封装了一个更新方法,凡是预览区的编辑操作都必须走这个封装,确保光标位置永远被正确映射。这个问题修完之后,我再也没遇到过光标跳回开头的情况。
5.2 中文输入法组合态问题与 IME 处理
第二个坑和中文输入法有关。在写 Markdown 文档时,我最常输入的是中英文混合内容。在输入拼音的过程中,编辑器如果错误地处理了组合态文字,就会出现候选词闪现、文字重复、甚至光标乱跳的情况。
这个问题的根源在于,编辑器内核需要区分"用户正在输入(组合态)"和"输入完成(确认态)"两种状态。如果编辑器在组合态时就去更新文档模型,相当于同一个拼音被当成了最终文字来处理,自然会产生错乱。
CodeMirror 6 对 IME 的支持整体上做得不错,但在一些自定义插件里仍然有边界问题。我这里踩的坑主要出在我的自动补全插件上:在输入拼音的过程中,补全弹窗出现了,但它选择补全项时错误地把提示内容插到了正在组合的拼音中间,导致文字错乱。解决方案是给补全插件增加一个 IME 状态监听:当检测到当前正在输入法组合态时,自动关闭补全建议的插入功能,等到确认输入之后再恢复。这个改动虽然小,但对中文用户的体验提升非常明显。
5.3 大型文档的渲染性能瓶颈:虚拟滚动替换全量渲染
第三个坑是大型文档的性能。最初版本上线后,我用一篇三万字的技术文档做测试,打开文件时发现要卡接近两秒,滚动的时候也有明显掉帧。
初步排查定位到问题在预览区的全量渲染。三万字渲染成 HTML,DOM 节点数超过了 1.5 万,浏览器在构建和布局这些节点时就已经很吃力了。再加上我用了 React 来做 DOM 更新,初次挂载和状态变更时的协调成本非常高。
修复过程我写下来供参考:先用 Performance 面板做了首字节到首屏的全链路分析,发现 70% 的时间花在 DOM 构建,25% 花在样式计算,其余是 JavaScript 解析时间。确定瓶颈后,我引入了虚拟滚动方案,把预览区改造成了"窗口化"组件:只渲染可视区域内的内容块,并加上上下各 200 像素的缓冲区,让快速滚动不会出现白屏。这个改造做完之后,打开同一篇三万字文档的时间从 2 秒降到了 200 毫秒左右,滚动的流畅度也基本和普通网站没有差别了。
5.4 导出 PDF 的分页问题:CSS Paged Media 实战
导出 PDF 的功能也是踩坑重灾区,最典型的问题是分页。最初我直接使用浏览器打印功能导出 PDF,结果非常糟糕:长表格被拦腰截断,代码块从中间断开,标题恰好落在页面底部,内容和页眉压在一起。
这些问题的本质是:普通网页的 CSS 没有考虑"分页"这个维度,而打印和 PDF 导出需要的是另一种样式系统——CSS Paged Media。修复方案的核心是三条 CSS 规则:给表格和代码块设置break-inside: avoid(内部不跨页断)、给标题设置break-after: avoid(标题和后面的内容保持在同一页)、给列表项设置break-inside: avoid(避免列表项跨页断)。此外还需要处理页面边距的 margin-box 内容,比如页码、文档标题、日期,这些可以通过@page规则来自动生成。
这一步做完之后,导出的 PDF 质量有了质的提升。但还有最后一个细节问题:当单个表格超过一页时,强制不跨页会导致表格被整体推到下一页,前面留下大块空白。解决方案是在表格上设置"允许跨页但重复表头"的策略,让表格在跨页时自动在下一页重新打印表头行。这个细节让我又花了一个周末调试,但效果确实值得。
6. 编辑器实测:和主流工具的对比参考
6.1 核心场景横向对比
开发接近完成时,我做了一轮横向对比测试。我选取了自己最常用的三个场景作为测试用例:写一篇带大量代码块和公式的技术博客、整理一份混合了表格和图片的会议纪要、维护一个长期更新的项目文档仓库。以下是和其他主流编辑器的大致对比结果,非严格性能测试,仅供参考:
| 场景 | 我的编辑器 | 其他主流编辑器A(即时渲染型) | 其他主流编辑器B(纯编辑型) |
|---|---|---|---|
| 大文档流畅度 | 流畅 | 中 | 流畅 |
| 表格编辑体验 | 可视化浮层,方便 | 支持但操作稍重 | 纯源码编辑,较差 |
| 图片路径容错 | 强,自动匹配 | 中 | 弱 |
| 数学公式渲染速度 | 快(KaTeX) | 中(部分用 MathJax) | 不支持 |
| 导出PDF排版 | 好,分页规则完善 | 依赖系统打印 | 不导出 |
| 跨平台一致性 | 好(Electron) | 受限于平台 | 受限于平台 |
| 离线使用 | 完全离线 | 部分支持 | 完全离线 |
6.2 意料之外的需求:从搜索引擎热词看用户真实痛点
在开发调试和收集反馈的过程中,我特别关注了网上对 Markdown 相关的搜索关键词,这些关键词直接反映了用户的真实痛点。
搜索量比较高的几个关键词包括:"markdown换行"、"markdown 数学公式"、"markdown表格转换excel"、"markdown图片路径"、"markdown转word"、 "vscode用markdown转pdf" 等等。这些搜索词背后是一大类被基础教程忽略的问题:很多人学会了 Markdown 基础语法,但一进入真实工作场景就卡住了。比如"markdown换行"——用过 Markdown 的人都知道,在标准语法里,单个回车是不会产生段落的换行的,必须在行尾加两个空格或者用一个空行。这个语法设计对新人来说非常不直观,我因此在编辑器的状态栏里加了一个实时的格式提示,当检测到行尾有"两个空格加回车"的写法时,会以视觉标记提示用户,避免排版意外。
"markdown转word工作流"和"markdown pdf"这类需求,前面已经说过,我在导出能力上做了针对性设计。而 "markdown表格复制" 这种搜索词,恰恰印证了我做表格可视化编辑和剪贴板双向转换的方向是对的。搜索引擎里最常被搜索的问题,往往就是用户在使用中摔得最惨的地方。
6.3 编辑器内置的"新手不犯错"设计
既然看到了这么多新手的痛点,我在功能设计时就有意加入了几个"防犯错"的辅助机制,这是很多编辑器没有的。
第一个是完整的格式校验器。当文档里存在 Markdown 语法错误或者容易导致渲染问题的隐患时,编辑器不会只给出一个笼统的错误提示,而是精确定位到具体行,告诉用户问题出在哪、标准语法应该怎么写。比如没关闭的代码块、未配对的数学公式分隔符、错误的表格列数。这个校验器不是阻塞式的,而是以波浪线标记的方式在编辑区展示,用户可以自己决定要不要修正。
第二个是导出前的预览确认。用户点击导出 PDF 或者 Word 时,编辑器会先展示一个"导出预览"面板,以缩略图方式展示每一页的样子,用户可以直接预览分页、检查是否有表格断裂、图片是否正常加载,确认没问题再真正导出。这比"导出后才发现问题"节省了非常多来回折腾的时间。
第三个是版本快照机制。我遇到过太多次意外覆盖的情况——源文件还在,但自己不小心改错了内容,又没有及时撤销回去。为此我做了一个轻量级的快照功能:每五分钟自动保存一份增量快照,可以回溯到任意时间点恢复内容。注意,这不依赖任何云服务,所有快照都存放在本地文件夹里,保证数据始终掌握在用户自己手中。
7. 开发之外的一些心得
编辑器的主体功能做完了,但开发和迭代的过程还在继续。这里说一些开发之外的经验,可能会对想自己做类似工具的人有参考价值。
关于"做一款自己的工具"这件事,我最大的体会是:需求量不一定要多大,但一定要是你自己每天都用、而且用得很痛苦的场景。正因为是我自己每天在用,每一个细小的摩擦都会被我感知到,也正因为是自己要用,我在解决这些痛点时才有足够的耐心去打磨。如果用的人少,产品可能就没必要做了——这句话放在商业化产品上适用,但放在个人工具上完全相反:哪怕全世界只有你一个用户,只要它让你每天的工作更舒服,这个工具就值得做。
对于技术栈的选择,我的建议是优先选择自己最熟悉、社区最成熟的技术,而不是盲目追新。Electron 和 CodeMirror 6 都是非常成熟的项目,虽然它们不是那种"酷炫"的技术,但稳定性和生态给了我非常大的确定性。做工具类产品,稳定性就是最大的友好。
最后分享一个小建议:如果你也有自己动手开发工具的想法,不用一开始就规划得特别宏大。我最初的需求其实只是想解决"表格编辑和图片路径"两个问题,后来才一点一点扩展出了导出、主题、校验这些功能。工具是慢慢长出来的,不是一次性设计出来的。先用最小方案解决自己的痛,然后在实际使用中逐步迭代,这是我认为最可持续的开发路径。