自研排版引擎四个月:我想做开源的 Google Docs / WPS
做了三年 ProseMirror 相关产品,我经常遇到各种转换问题:word 导入某某样式丢失、样式错误,导出样式不对,某个功能不支持,能不能做分页,能不能支持......有时候也会很无力,prosemirror 底层不支持呀,转换要靠 html 做中转,本来就有损耗,实在是有心无力。思考了一段时间后,我打算靠自己这几年积累的经验,借着 AI 模型发展这股风,把我想要的硬骨头彻底啃下来。
这些转换和兼容问题,让我开始重新考虑编辑器的底层:要把在线文档做得更接近 Word,光把段落、表格和图片编辑好还不够,还得掌握它们如何排在页面上。表格跨页怎么处理,修改前面的文字后,后续页面如何重排,光标和选区又该落在哪里?这些都需要一套能够自己控制的排版机制,也成为我决定自研排版引擎的直接原因。
2026 年 6 月,我开始做 QuireKit。四个月后,它有了可以实际体验的分页文档编辑器,也逐渐长出了块编辑器、Markdown 编辑器和一些能够独立使用的组件。
这篇文章记下这段工作的起点,以及目前做到了哪里。
先说明范围:QuireKit 是供开发者构建文档产品的编辑器框架,目前处于 alpha 阶段。 框架、排版引擎和基础编辑器采用 MIT 许可;Word 兼容、专业导出等能力另有商业组件。它离完整替代 WPS 或 Google Docs 这样的产品,还有许多工作要做,但目前已经具备 word 85% 以上常用功能的兼容,它有一个主要的特色就是尽量可以通过页面操作出支持的格式,而不是仅兼容导入展示,支持 350+ 页面操作,这一点目前个人没有在市面上找到能打的替代品。
分页编辑,为什么要自己掌握排版
对于连续滚动的文章、笔记和知识库,浏览器的 DOM 排版很合适。分页文档需要处理另一组约束:纸张大小、页边距、跨页表格,以及内容变化后整份文档的重新分页。
如果分页只是编辑完成后的显示效果,可以先让浏览器排版,再测量和切分。但在实时编辑中,输入、选区、分页和导出需要始终对得上。我希望这些操作能够使用同一份排版结果。
因此,QuireKit 的分页视图自己处理断行、分页和位置计算,再把结果交给 Canvas 或 SVG 绘制。点击文字时的位置判断、光标和选区,也要跟这份结果一致。
图片加载中
原帖中的分页编辑器演示。图中完整的 Word 操作界面包含商业组件,不能据此把所有演示功能都理解为 MIT 开源部分。
目前的演示已经能看到表格跨页、重复表头,以及公式、流程图、思维导图等内容参与页面排版。长文档会按可见页进行绘制,减少屏幕外的绘制工作;实际流畅程度仍然取决于文档复杂度、字体和设备。
DOCX 导入导出、PDF/OFD 版式导出则属于另外一层。后者复用编辑器的分页与绘制结果,让屏幕和导出文件共用排版依据。这里的“自研”指排版及导出管线,也会使用第三方文件编码库,并不意味着从文件格式到所有依赖都重新写了一遍。
ProseMirror 留下了什么
三年的 ProseMirror 使用经验没有被丢掉。文档模型、事务、命令、插件、历史记录和协同机制,仍然是这条编辑路线的基础。
@quirekit/core 提供 Tiptap 风格的扩展接口,重点是让编辑能力与具体视图分开。同一套文档和扩展机制,可以面向不同的排版需求:
| 视图 | 主要用途 |
|---|---|
| Canvas / SVG 分页视图 | 需要纸张和分页的文档编辑 |
| HTML 连续视图 | 块编辑器、笔记和知识库,由 QuireKit 管理输入、光标与选区 |
| DOM 视图 | 延续 ProseMirror 的 DOM 编辑方式 |
兼容 Tiptap 是一个明确的方向,但不能理解成所有扩展只换一个 import 就一定能用。涉及 DOM 操作、NodeView 或特定视图行为的扩展,仍然需要检查和适配。
块编辑器也从这套分离中受益。例如,选中文字之后再去工具栏输入链接,编辑区域会失去焦点,但选区的显示和命令目标仍然需要保留。让选区由编辑器管理,才能把这些交互接起来。
Markdown 走的是另一条路
Markdown 的需求又不同。使用者往往关心的不只是显示效果,还有原文:空格、换行、列表写法,以及没有修改过的内容能否保留下来。
所以我没有把 Markdown 编辑强行放进富文本模型,而是基于 CodeMirror 和 Lezer 做了 MSFE,让 Markdown 原文本身成为文档。MD Editor 是构建在它上面的成品编辑器。
它提供四种视图:
文档:尽量收起语法标记,直接编辑呈现出来的内容。
实时预览:编辑时结合语法与渲染结果。
源码:直接编辑 Markdown 文本。
阅读:查看当前内容的排版结果。
前三种视图操作的是同一份源码,视图切换不需要把全文转换成富文本 JSON 再序列化回来。未被编辑操作触及的文本可以保留原样;格式化和块操作本身需要修改的范围,仍会产生相应的源码变化。
图片加载中
这也让 Markdown 编辑器适合单独嵌入博客、评论区等场景,不必同时引入分页文档的整套能力。
一次性能优化,究竟减少了什么
做大文档编辑时,我发现一个重复工作的地方:解析器已经复用了没有变化的语法片段,后面的公共比较层却还会重新遍历它们。
我给这条链路补充了“复用证明”。只有解析器实际确认复用、且坐标映射仍然吻合的区间,公共比较层才整体跳过;边界和不能证明的部分继续检查。
在当时固定的 1 MiB Markdown 文档、连续 20 次普通输入测试中,记录到了下面的工作次数:
| 工作 | 优化前 | 优化后 |
|---|---|---|
| 新语法树片段访问 | 107,760 | 1,380 |
| 旧语法树路径比较 | 153,940 | 660 |
两项计数分别减少约 98.7% 和 99.6%。这说明重复比较明显减少,不能把它换算成整个编辑器快了 98%:解析、引用收集、布局和呈现还有各自的开销。
同一组配对测试里,1 MiB 文档编辑的输入接受耗时 P95 从 11.4ms 降到 10.9ms,改善远小于树访问次数的降幅。这里记录的是特定夹具下的结果,也不代表所有设备和输入场景。
对我来说,这次优化的价值是把重复工作找出来,并在保持解析结果一致的前提下减少它。性能结论需要同时看工作次数、耗时和正确性。
中文输入法:临时文字也要参与排版
在 Canvas 上画出文字之后,还要让人能自然地输入文字。中文输入法是这里最难处理的部分之一。
拼音尚未确认时,预编辑文字已经需要出现在正文里,并把后面的内容推开;但这些临时变化又不应该逐次进入撤销历史或发送给协作者。
QuireKit 通过隐藏的输入代理接收浏览器事件。组合输入期间,用临时文档状态参与排版和绘制;确认输入后,再通过正常事务写入正式文档。
图片加载中
浏览器事件还有不少细节:事件数据可能为空,组合结束与输入事件的顺序可能不同,同一段确认文字也可能被重复处理。这部分被整理为独立的 @quirekit/ime 包,让不同视图共用输入协议,而不是各自维护一套处理。
这仍然需要持续拿真实操作系统、浏览器和输入法验证。通过自动化事件测试,不等于所有真实输入环境都已经覆盖。
为什么还做了一套 Vue UI
原帖里有读者问:既然编辑和输入都自己处理了,为什么还依赖 Vue?
Vue 主要用于成品编辑器的界面。编辑内核与排版引擎不依赖 Vue,也不要求使用 QuireKit 的 UI。字体菜单、表格工具、属性面板和各种弹窗,则需要一个实际可用的界面层来承接。
为了让分页文档、块编辑器和 Markdown 编辑器共用颜色、字号、间距及交互方式,我做了 QuireKit Design,基于 Vue 和 UnoCSS。它同样可以单独用于其他项目:design.quirekit.com。
公式、思维导图、白板、流程图、图表等能力也按包组织。其中会使用成熟的上游库和适配实现,不是所有周边能力都从零开发。我的重点是把它们接到一致的文档与编辑流程里,同时保留单独使用的可能。
开源和商业组件怎样区分
QuireKit 采用开放核心的方式。选型时,最好按自己实际需要的包确认范围。
| 范围 | 许可与使用方式 |
|---|---|
| 编辑内核、Canvas/SVG 排版引擎与视图、基础分页壳层、块编辑器、MSFE、MD Editor、UI,以及相应的开源周边包 | MIT,可用于开源或闭源项目,保留许可证与版权声明 |
| 完整 Word 文档语义、DOCX 往返、专业 PDF/OFD 导出,以及另行提供的专业界面与服务 | QuireKit 商业组件;开发和评估可免费使用,生产使用按对应授权条款处理 |
官网的完整演示可能同时装配两类组件。具体以每个包的许可证和公开说明为准,不能仅凭演示中出现一个按钮判断它属于哪一层。当前范围可以查看 GitHub 仓库的许可说明。
从一份真实的分页文档开始试
如果你也在做需要分页的文档产品,可以先打开 Classic 文档编辑器演示,用一份自己的文档试一试:修改段落、让表格跨页,看看光标、选区和后续页面如何跟着变化。比起功能清单,这些实际操作更能说明排版引擎目前做到了什么。
确认编辑体验符合需求后,再根据需要接入编辑内核、排版视图或成品编辑器。具体接入方式见 文档,开源代码和许可范围见 GitHub。完整演示同时包含开源与商业组件,选型时需要核对自己实际使用的包。
当前仍是 alpha,实际项目应锁定版本,并用自己的文档验证输入、分页、导入导出和扩展行为。我会继续记录排版和编辑中的具体问题。如果试用时遇到问题,带上一份最小文档、操作步骤,以及浏览器和输入法信息,会很有帮助。
本文由我在 2026 年 10 月 28 日发布的 V2EX 原帖整理而来。博客版调整了结构,并补充了技术范围、性能数据口径和许可说明。
讨论
留下你的想法
登录评论无需登录也可以评论。友善交流,审核后公开。