emoji 导致协作编辑器崩溃根因
推荐指数 42.0 NO. 018 · 2026.05.17
发布2026/05/16Score65Comments35
为什么值得看
开发者迁移 TipTap 协作编辑器时,emoji 输入后神秘消失,追踪发现是 JavaScript 字符串的 invalid surrogate pairs 在 ProseMirror 事务转换中被静默丢弃。这类 Unicode 边界 bug 在富文本场景极易被忽视,却会导致数据无声丢失。
媒体预览
编辑判断
这个 bug 的阴险之处在于完全静默失败——不会抛错,只是内容消失,用户甚至以为是自己的操作问题。JavaScript 的 UTF-16 字符串设计让 lone surrogate 成为合法存在,但大多数编辑器框架(包括 TipTap/ProseMirror 栈)在协作同步的 JSON 序列化或 OT 转换时会直接过滤掉 invalid pairs。
如果你在做实时协作产品(文档、白板、代码),建议立刻检查你的 CRDT/OT 库对 Unicode 的处理策略,尤其是 WebSocket 传输前后的字符串校验。一个防御性做法是在输入层强制 NFC 规范化并拒绝 lone surrogate,而不是依赖下游框架的容错。
社区反馈
意见分歧 35 条评论
核心争论:Unicode 字符串处理中 UTF-16 代理对的设计缺陷与跨语言兼容困境
Once I ran into this it became hard to treat strings “normally” in any situation or, alternatively, I’d force hard encoding requirements in the domain. Regardless, handling grapheme clusters properly is hard and easy to get wrong. I recently ported a program from python to rust and the original auth
You are reminding me we also circled an issue at one point where a backend system in Python needed to agree on the same character count length of a piece of content was the client (JavaScript). Another place Intl.Segmenter would've helped. If I'm remembering correctly, we briefly explored a solution
it's good to know about surrogate pairs in unicode. It was new to me too when being part of tracking down incomplete unicode flags in the (excellent) phanpy mastodon client. Author went for Intl.Segmenter too: https://github.com/cheeaun/phanpy/issues/1491