编辑器选型决策(CodeMirror 6)

2026-04-16 14:00:00 docs MDMS 开发组 3561 字

一、决策结论

Markdown 编辑器选 CodeMirror 6,加载方式选 ESM importmap + esm.sh。

涉及的备选方案:

| 方案 | 评估 | 结论 | |------|------|------| | CodeMirror 6 + importmap | 官方推荐路线,HackMD 同款 | ✅ 采用 | | CodeMirror 6 + 社区 UMD bundle | 简单但生命线交给小项目 | ❌ 否决 | | CodeMirror 5 + EasyMDE | 现成工具栏,CDN 友好 | ❌ 否决(迁移成本) | | Monaco Editor | 移动端不可用,体积过大 | ❌ 否决 | | Block 编辑器(TipTap/ProseMirror) | 违背 MD 第一公民 | ❌ 永不启用 | | Toast UI / Vditor | 思源同款,移动端好但是 Block 模型 | ❌ 否决 |

二、为什么否决 Monaco Editor

Monaco 是 VS Code 同款,工程上很强,但和 MDMS 不匹配:

  1. 移动端不可用——Microsoft 官方文档明确说不支持移动端。MDMS 已确立「移动端可用性」公理,Monaco 直接出局
  2. 体积过大——核心 3MB+,gzip 后 ~700KB,比 CodeMirror 大十倍。MDMS 后台编辑器作者每次打开都要加载,对中国服务器和弱网用户不友好
  3. 为写代码设计,不为写作——IntelliSense、TypeScript 服务、多光标这些是亮点,但 MDMS 写 Markdown 用不上。Markdown 渲染、表格对齐、列表续行这些写作细节 Monaco 反而不如 CodeMirror
  4. Yjs 集成相对小众——y-monaco 主要用于远程结对编程而非协同文档。HackMD/Notion-clone 类项目都用 CodeMirror + Yjs 这套

Monaco 适合做的事 MDMS 都不做:在线 IDE(CodeSandbox)、复杂代码审查(GitHub PR)、配置文件 schema 校验等。

三、为什么否决 Block 编辑器

Block 编辑器(TipTap、ProseMirror、Editor.js 等)的数据真相是 JSON 块树,不是 Markdown。这违背 MDMS 两条核心原则:

  1. MD 第一公民——MD 文件作为数据真相是 v2.0 七正交原则第 4 条
  2. 智能体友好——智能体读 MD 写 MD 的工作流要求 MD 是真相,Block 反序列化为 MD 有损

虽然 Block 编辑器体验在桌面上很爽(Notion 风格的 / 斜杠命令、拖拽块),但桌面爽 = 移动端拉。Notion 在手机上至今体验不达标。MDMS 既要 MD 真相,又要移动端可用,Block 编辑器双不满足。

例外条件:除非未来证明 Block ↔ MD 双向序列化无损往返,且移动端体验对标 HackMD,否则永不启用。

四、为什么否决 CodeMirror 5

CM5 现在能用,但是踩在已经开始萎缩的栈上:

  1. HackMD 已在迁离 CM5——HackMD next 版本用 CM6
  2. Yjs 主推方向是 y-codemirror.next(CM6 版),y-codemirror(CM5 版)维护不积极
  3. CM5 模块化差——加扩展是改全局 CodeMirror 对象,CM6 是树摇友好的 ES module
  4. 三五年后再迁的成本远高于现在直接上 CM6

唯一让 CM5 显得诱人的是 EasyMDE 这种现成工具栏包装。但工具栏可以自己加,迁移成本不能省。

五、为什么 importmap 而非 UMD bundle

CodeMirror 6 官方不提供 UMD 单文件 bundle,因为它设计时就只支持 ESM。社区有人打了 UMD bundle 传到 npm/CDN,但风险高:

  1. 生命线交给小项目——这些 bundle 项目 star 数都不高,最后更新可能在半年前。如果哪天它不维护了或 CDN 挂了,MDMS 要自己打包 CM6 ——回到 importmap 方式或更糟
  2. 跟不上 CM6 新版本——升级 CM6 要等社区 bundle 跟进
  3. 加 CM6 插件受限——bundle 里没打的插件用不了
  4. Yjs 接入受限——bundle 不带 y-codemirror.next 集成,Yjs 上线时被迫退化为 importmap

importmap 是 CM6 官方推荐路线,浏览器原生支持,看起来不熟悉但本质就是一段 JSON 配置,写一次复制到模板里就好。这是一次性认知成本,换来后续所有升级、插件、Yjs 接入的顺畅。

六、esm.sh 的 CDN 风险

esm.sh 是个第三方 CDN。可能的故障模式:

  • esm.sh 抽风某个文件加载失败(5-10 个文件并行加载,任何一个挂都失败)
  • 整个 esm.sh 服务挂了(罕见但可能)
  • 网络抽风(中国大陆访问 esm.sh 偶尔会慢)

降级路径(已实现):CM6 加载失败时,原 textarea 自动兜底,作者依然可编辑。仅失去语法高亮和 [[ 自动补全。这是「降级保完备性」原则的具体落地。

未来加固方案

  • 把 esm.sh 那几个 CM6 文件下载到本地 /static/codemirror/ 自己服务,彻底脱离外部依赖
  • importmap 同时声明多个 fallback URL(语法繁琐但可行)
  • 切换到 jsdelivr 的 ESM CDN 备选

七、移动端公理的影响

「移动端可用性」从 v2.0.4 起作为 PHILOSOPHY 公理写入。这条公理对编辑器的具体要求:

  1. 触屏选择必须工作(CM6 ✅)
  2. 虚拟键盘出现时编辑器不被挤压(v2.0.4 用视口 55% 高度策略)
  3. 不能依赖鼠标悬停的功能(CM6 自动补全用键盘和触摸都能选 ✅)
  4. 不能依赖键盘快捷键的核心功能(CM6 默认所有命令都有触摸路径 ✅)
  5. 字体大小至少 13px(v2.0.4 设了 fontSize: "13px")
  6. 编辑器加载时间不能太长(CM6 首次 ~500KB,后续走浏览器缓存)

八、未来演进路径

v2.0.5:Yjs 实时协同

  • 新增 import:y-codemirror.next
  • 编辑器扩展数组里加 yCollab(ytext, awareness)
  • 后端加 WebSocket 通道(Gorilla WebSocket)
  • 接同款编辑器栈上的协同——HackMD 这套已经验证到生产级别

v2.0.6+:CSS 编辑器和 HTML 模板编辑器

  • css_edit.html / template_edit.html 也升级到 CM6
  • 引入 @codemirror/lang-css@codemirror/lang-html
  • importmap 加几行即可

v2.0.7+:编辑器辅助功能

  • 表格快速插入快捷键
  • 图片上传集成(依赖 v2.0.x 的图片管理插件)
  • Mermaid 图表预览
  • 数学公式渲染(KaTeX)

每一条都是在 CM6 基础上加扩展,不需要重写编辑器底层。这就是「保留可回退起点」的复利。

九、技术实现位置

  • 文章编辑:templates/admin/article_edit.html 第 60+ 行
  • 文档编辑:templates/admin/doc_edit.html 末尾
  • 插件市场标记:handler/handler.go 的 plugins 数据
  • 文章列表 API(供 [[ 补全):GET /api/articles/list,handler 在 handler/handler.go
  • 兜底原则:textarea 始终保留在 DOM 里,CM6 加载成功才隐藏

设计原则:编辑器是体验放大器,不是阻塞项。CM6 失败不应阻止作者写文章。

来源:ChatDLM 原创 / 作者:MDMS 开发组 / 发布时间:2026-04-16 14:00:00 / ChatDLM 版权所有

热门话题

最新话题