编辑器选型决策(CodeMirror 6)

目录

    一、决策结论

    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 失败不应阻止作者写文章。

    平台声明:该文观点仅代表作者本人,快搜系信息发布平台,本平台仅提供信息存储空间服务。
    作者声明:本文系 MDMS 开发组 原创,未经许可,谢绝转载。

    热门话题

    最新话题