---
title: "自研排版引擎四个月：我想做开源的 Google Docs / WPS"
language: zh
description: "做了三年 ProseMirror 相关产品后，我用四个月开发了 QuireKit，尝试为 Google Docs / WPS 这样的在线文档产品构建开源基础。记录分页、Markdown 和中文输入法的实现，以及当前的性能结果、MIT 开源范围与商业组件边界。"
cover: "https://yishuai.wang/media/ced698cb-d454-4eeb-87f5-5ac4e6f57938"
url: "https://yishuai.wang/articles/a6869748-82f5-4ab8-8bdd-331a870e855e"
translations: [{"language":"zh","url":"https://yishuai.wang/articles/a6869748-82f5-4ab8-8bdd-331a870e855e"}]
published: 2026-10-30T07:00:56.571Z
updated: 2026-10-30T07:00:56.571Z
---

# 自研排版引擎四个月：我想做开源的 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 绘制。点击文字时的位置判断、光标和选区，也要跟这份结果一致。

![QuireKit 分页文档编辑器的亮色与深色界面](/media/000dbedf-59fa-45b1-93ef-a4e71f865cd1)

*原帖中的分页编辑器演示。图中完整的 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 在文档、实时预览和源码视图中的呈现](/media/58882e5d-fdb2-4964-8c06-f4549ed6e67f)

这也让 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 通过隐藏的输入代理接收浏览器事件。组合输入期间，用临时文档状态参与排版和绘制；确认输入后，再通过正常事务写入正式文档。

![中文输入法的预编辑状态与正式文档事务](/media/cf350103-73dd-45c1-8b8f-5cb00eb0cbe0)

浏览器事件还有不少细节：事件数据可能为空，组合结束与输入事件的顺序可能不同，同一段确认文字也可能被重复处理。这部分被整理为独立的 `@quirekit/ime` 包，让不同视图共用输入协议，而不是各自维护一套处理。

这仍然需要持续拿真实操作系统、浏览器和输入法验证。通过自动化事件测试，不等于所有真实输入环境都已经覆盖。

## 为什么还做了一套 Vue UI

原帖里有读者问：既然编辑和输入都自己处理了，为什么还依赖 Vue？

Vue 主要用于成品编辑器的界面。编辑内核与排版引擎不依赖 Vue，也不要求使用 QuireKit 的 UI。字体菜单、表格工具、属性面板和各种弹窗，则需要一个实际可用的界面层来承接。

为了让分页文档、块编辑器和 Markdown 编辑器共用颜色、字号、间距及交互方式，我做了 QuireKit Design，基于 Vue 和 UnoCSS。它同样可以单独用于其他项目：[design.quirekit.com](https://design.quirekit.com)。

公式、思维导图、白板、流程图、图表等能力也按包组织。其中会使用成熟的上游库和适配实现，不是所有周边能力都从零开发。我的重点是把它们接到一致的文档与编辑流程里，同时保留单独使用的可能。

## 开源和商业组件怎样区分

QuireKit 采用开放核心的方式。选型时，最好按自己实际需要的包确认范围。

| 范围 | 许可与使用方式 |
| --- | --- |
| 编辑内核、Canvas/SVG 排版引擎与视图、基础分页壳层、块编辑器、MSFE、MD Editor、UI，以及相应的开源周边包 | MIT，可用于开源或闭源项目，保留许可证与版权声明 |
| 完整 Word 文档语义、DOCX 往返、专业 PDF/OFD 导出，以及另行提供的专业界面与服务 | QuireKit 商业组件；开发和评估可免费使用，生产使用按对应授权条款处理 |

官网的完整演示可能同时装配两类组件。具体以每个包的许可证和公开说明为准，不能仅凭演示中出现一个按钮判断它属于哪一层。当前范围可以查看 [GitHub 仓库的许可说明](https://github.com/quirekit/quirekit#licensing)。

## 从一份真实的分页文档开始试

如果你也在做需要分页的文档产品，可以先打开 [Classic 文档编辑器演示](https://quirekit.com/demo/classic-editor/)，用一份自己的文档试一试：修改段落、让表格跨页，看看光标、选区和后续页面如何跟着变化。比起功能清单，这些实际操作更能说明排版引擎目前做到了什么。

确认编辑体验符合需求后，再根据需要接入编辑内核、排版视图或成品编辑器。具体接入方式见 [文档](https://docs.quirekit.com)，开源代码和许可范围见 [GitHub](https://github.com/quirekit/quirekit)。完整演示同时包含开源与商业组件，选型时需要核对自己实际使用的包。

当前仍是 alpha，实际项目应锁定版本，并用自己的文档验证输入、分页、导入导出和扩展行为。我会继续记录排版和编辑中的具体问题。如果试用时遇到问题，带上一份最小文档、操作步骤，以及浏览器和输入法信息，会很有帮助。

---

*本文由我在 2026 年 10 月 28 日发布的 [V2EX 原帖](https://www.v2ex.com/t/1252425)整理而来。博客版调整了结构，并补充了技术范围、性能数据口径和许可说明。*

