
tiptap 协作编辑实战用 Yjs Hocuspocus 三步搭建多人实时协同编辑器【免费下载链接】tiptapThe headless rich text editor framework for web artisans.项目地址: https://gitcode.com/GitHub_Trending/ti/tiptap做 tiptap 协作编辑说白了就是三件事一个所有客户端共享的 Yjs 文档Y.Doc、一个负责中继同步的 Hocuspocus 协同后端、以及把编辑器接上这个共享模型的协作扩展。本文带你把这套 tiptap 多人编辑方案完整落地——从一键拉起协作后端到写出一个能多人共写团队周报的编辑器再把远程光标、权限、离线重连这些生产细节逐一补齐。先搞清楚两个人同时敲字为什么不会互相覆盖很多人第一反应是得自己写锁或者合并逻辑吧。不用。tiptap 的协作扩展底层接的是 Yjs它把文档建模成 CRDT无冲突复制数据类型A 和 B 各改各的变更带着来源标识传播最终在每一端收敛成同一份内容不需要谁赢的裁决。落到 tiptap 上分工是这样的源码分别在 packages/extension-collaboration/ 和 packages/extension-collaboration-caret/Y.Doc共享文档模型所有编辑数据的唯一事实来源Collaboration 扩展在编辑器与 Y.Doc 之间架桥负责双向同步并且自带基于 Yjs 的撤销/重做注意它和tiptap/extension-undo-redo不兼容二选一TiptapCollabProvider来自 hocuspocus/provider客户端与服务端之间的网络通道CollaborationCaret 扩展把远程用户的位置渲染成带名字的光标。一键拉起协作后端Hocuspocus 部署两种姿势Hocuspocus 是 tiptap 官方推荐的开源协同后端部署方式二选一直接用官方托管服务开发调试阶段最省事客户端配置里带上appId: 7j9y6m10即可连上自己起服务上线时更可控。Docker 一行命令就能跑docker run -p 1234:1234 -e HOCUSPOCUS_NAMEmy-collab-server ueberdosis/hocuspocus起完服务后客户端把连接目标指向你的ws://your-server:1234即可。最快连通共享文档的姿势客户端三步走安装依赖npm install tiptap/core tiptap/extension-collaboration tiptap/extension-collaboration-caret yjs hocuspocus/provider然后按顺序做三件事每步只有一小段代码第一步创建共享文档。它是数据中枢编辑器后续所有同步都围绕它展开import * as Y from yjs const sharedDoc new Y.Doc()第二步建立到 Hocuspocus 的连接。name是房间标识——房间名相同的客户端看到的永远是同一份内容这也是多人编辑同一篇文档的实现方式import { TiptapCollabProvider } from hocuspocus/provider const syncChannel new TiptapCollabProvider({ appId: 7j9y6m10, // 自部署时换成你自己的服务地址 name: weekly-report-2026-w35, document: sharedDoc, }) // 盯住连接状态做 UI 反馈 syncChannel.on(status, ({ status }) { console.log(当前连接状态, status) // connecting / connected / disconnected })第三步让编辑器挂上协作扩展。这里有个必须记住的配置StarterKit里的history要关掉因为历史记录交给 Yjs 的 undo 栈管理两边同时维护只会打架import StarterKit from tiptap/starter-kit import Collaboration from tiptap/extension-collaboration import CollaborationCaret from tiptap/extension-collaboration-caret extensions: [ StarterKit.configure({ history: false }), Collaboration.configure({ document: sharedDoc }), CollaborationCaret.configure({ provider: syncChannel, user: { name: 小林, color: #70CFF8 }, }), ]到这里tiptap 实时协同的最小闭环已经通了。完整例子两个人一起写团队周报下面用 Vue 3 的script setup写一个周报协作编辑器比项目里的 官方协作编辑演示 更贴近业务两个人各自打开同一房间边写边看到对方的进度和光标。template section classreport-editor menu-bar :editoreditor / editor-content :editoreditor / footer classreport-editor__footer span :classsync-dot sync-dot--${status} / span v-ifstatus connected{{ onlineCount }} 人在线/span span v-else连接中…/span button clickrenamePeer改名{{ peer.name }}/button /footer /section /template script setup import { computed, onBeforeUnmount, ref } from vue import { TiptapCollabProvider } from hocuspocus/provider import Collaboration from tiptap/extension-collaboration import CollaborationCaret from tiptap/extension-collaboration-caret import StarterKit from tiptap/starter-kit import { Editor, EditorContent } from tiptap/vue-3 import * as Y from yjs import MenuBar from ./MenuBar.vue const peer ref({ name: 工程师小林, color: #70CFF8 }) // 共享文档 连接房间名决定和谁同桌 const sharedDoc new Y.Doc() const syncChannel new TiptapCollabProvider({ appId: 7j9y6m10, name: weekly-report-2026-w35, document: sharedDoc, }) const status ref(connecting) syncChannel.on(status, ({ status: s }) (status.value s)) const editor new Editor({ extensions: [ StarterKit.configure({ history: false }), Collaboration.configure({ document: sharedDoc }), CollaborationCaret.configure({ provider: syncChannel, user: peer.value, }), ], content: h2本周进展/h2p完成 Hocuspocus 协作后端部署/p h2下周计划/h2p联调光标同步与权限校验/p, }) // 在线人数直接读 collaborationCaret 扩展的 storage const onlineCount computed( () editor.value.storage.collaborationCaret.users.length, ) function renamePeer() { const name (window.prompt(换个名字) || ).trim().slice(0, 16) if (!name) return peer.value { ...peer.value, name } editor.value.chain().focus().updateUser(peer.value).run() } onBeforeUnmount(() { editor.value.destroy() syncChannel.destroy() }) /script几个值得留意的点在线人数不用自己维护心跳editor.storage.collaborationCaret.users会随 provider 的在线事件自动增减改名后要调updateUser命令否则别人看到的光标还是旧名字组件卸载时editor和syncChannel都要destroy()否则 WebSocket 会一直挂着。远程光标的观感靠两段 CSS 控制类名来自 collaboration-caret 扩展/* 远程光标本体 */ .collaboration-carets__caret { position: relative; border-left: 1px solid #0d0d0d; border-right: 1px solid #0d0d0d; margin-left: -1px; margin-right: -1px; pointer-events: none; word-break: normal; } /* 光标上方的用户名标签 */ .collaboration-carets__label { position: absolute; top: -1.4em; left: -1px; font-size: 12px; font-style: normal; padding: 0.1rem 0.3rem; border-radius: 3px 3px 3px 0; color: #0d0d0d; white-space: nowrap; user-select: none; }把 label 的背景色设成该用户的color就能看到带名字、带颜色的协同光标效果。避坑清单tiptap 多人编辑最常踩的三个坑坑一光标根本不显示。九成是漏了 CollaborationCaret 扩展或者user配置没传。内容同步和光标同步是两条独立链路——前者靠 Y.Doc后者靠 provider 的广播。只装了 Collaboration 的话你会看到内容动、光标不动。坑二撤销行为诡异或控制台报警告。Collaboration 扩展自带undo/redo命令快捷键 Mod-z、Mod-y 已绑定底层走 Yjs 的 undo 栈。此时如果再挂tiptap/extension-undo-redo两者会冲突。装协作扩展时把 StarterKit 的 history 关掉就好。坑三状态变化没反馈用户以为坏了。用 provider 事件做 UI 反馈syncChannel.on(status, ({ status }) { switch (status) { case connecting: return showSpinner() case connected: return hideSpinner() case disconnected: return showToast(连接断开正在重连…) } }) syncChannel.on(error, error { console.error(协作通道出错, error) if (error.code document_unavailable) { showToast(文档不存在或已被删除) } })走向生产权限、离线与性能细粒度权限。连接时向服务端提交令牌由 Hocuspocus 的认证钩子校验后再放行const syncChannel new TiptapCollabProvider({ appId: 7j9y6m10, name: weekly-report-2026-w35, document: sharedDoc, authentication: { token: 由后端签发的 jwt }, })断网不丢字。给 Y.Doc 挂一个 IndexedDB 适配器离线期间的编辑会落在本地恢复连接后自动补同步import { IndexedDBPersistence } from y-indexeddb const localStore new IndexedDBPersistence(weekly-report-2026-w35, sharedDoc) localStore.on(synced, () console.log(本地副本已就绪))大文档优化。协作历史由 Yjs 的 undo 栈管理文档越大、参与人越多历史越深。可以考虑按章节拆分成多个 Y.Doc 字段分别同步关闭暂时不需要的光标渲染避免全员光标广播。文档刚加载完成时还可以用documentLoaded事件做初始化检查syncChannel.on(documentLoaded, () { const fragment sharedDoc.getXmlFragment(default) if (fragment.length 0) { // 空房间注入模板内容比如周报骨架 } })写在最后回顾一下这条链路Y.Doc 存内容、Hocuspocus 传更新、Collaboration 扩展管编辑器同步、CollaborationCaret 管光标展示——四个角色各司其职冲突解决交给 CRDT你几乎不用碰底层算法。想深入的话从 packages/extension-collaboration/ 的collaboration.ts看起它只有两百多行能看到同步插件和 undo 插件是怎么拼进 ProseMirror 的。顺着这个底座协作批注基于 Yjs 共享注释字段、操作回放、活跃度统计这些玩法都能长出来——毕竟最难的那部分tiptap 和 Yjs 已经替你扛掉了。【免费下载链接】tiptapThe headless rich text editor framework for web artisans.项目地址: https://gitcode.com/GitHub_Trending/ti/tiptap创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考