协同集成
将 Univer 编辑器连接到协同服务,让多个用户编辑同一份文档。
协同集成连接前端编辑器和协同服务。例如,两位用户打开同一份表格,一方修改单元格,另一方能实时看到变化。
先按服务端集成准备后端,再完成下面的前端配置和文档加载。具体代码以 协同快速开始中的 Server、Web 示例为准。
连接已有编辑器
保留已有的 Web SDK 编辑器,按快速开始中的前端配置添加协同插件,并配置文档加载、修改提交和 WebSocket 连接地址。前后端包版本需要匹配。
示例中的固定用户和默认权限仅用于演示。正式接入时,让编辑器请求携带应用的登录信息,由后端确认用户身份和文档权限。许可证配置见使用许可证。
创建和打开同一份文档
“新建”和“打开”是两步不同的操作:
- 用户新建文档时,业务后端创建并保存文档,返回文档 ID。
- 前端用这个 ID 加载文档。再次打开或分享给其他用户时,仍使用同一个 ID。
API 中的 unitId 就是文档 ID。不要在每次打开时重新创建本地空白文档。
先设置当前用户,再加载文档。当前用户决定协同编辑的身份和本地离线缓存的归属,设置方式见身份与权限。
例如,已经注册 Sheets 和协同插件后,可以这样打开服务端表格:
import { UserManagerService } from '@univerjs/core'import '@univerjs-pro/collaboration-client/facade'univer.__getInjector().get(UserManagerService).setCurrentUser({ userID, name, avatar })const workbook = await univerAPI.getCollaboration().loadSheetAsync('your-document-id')if (!workbook) throw new Error('Document could not be loaded')不同产品的加载方式见 Sheets、Docs、Slides、Boards、Bases 和 PDFs。使用前确认对应产品与目标服务端版本支持该能力。
验证同步、保存和权限
用两个独立浏览器会话打开同一文档,例如普通窗口和无痕窗口。在一端修改内容,检查另一端是否收到;刷新页面、断线重连和服务重启后,再检查内容是否保留。
随后用编辑者、查看者和无权限用户验证访问规则。前端只读设置控制界面操作,服务端仍需拒绝无权限的读取和修改请求。
如果文档能打开但修改不同步,先检查 WebSocket 连接;如果重启后内容丢失,检查后端存储。相应配置见服务端集成。
添加导入导出、评论或历史记录
导入文件后多人编辑,需要将转换结果创建为协同文档,见导入导出集成。评论和历史记录需要各自的插件、后端服务及权限,按 Office 协同扩展模块接入。
历史面板的前端配置见 Sheets, Docs, Slides, Boards, Bases。PDFs 的支持范围见历史记录说明。
同步状态与重发
导入协同 Facade 后注册监听。CollaborationStatusChanged 会报告注册监听时已在协同的文档的当前状态,此后只报告变化,不连续重复相同状态。hasWritePending(unitId) 包含等待发送、等待确认和离线缓存的修改;省略 ID 时检查当前聚焦文档。WebSocket 已连接并不代表修改已经保存。
import '@univerjs-pro/collaboration-client/facade'const collaboration = univerAPI.getCollaboration()const statusSubscription = univerAPI.addEvent(univerAPI.Event.CollaborationStatusChanged, (event) => { console.info(event.unitId, event.status, collaboration.hasWritePending(event.unitId))})const resendSubscription = univerAPI.addEvent(univerAPI.Event.CollaborationChangesetResend, (event) => { console.info(event.unitId, event.reason, event.attempt, event.unsyncedMutationCount, event.nextDelayMs)})CollaborationChangesetResend 只在重发时触发,首次提交不触发。原因包括 ack_timeout、should_retry 和 reconnect;attempt、elapsedMs、unsyncedMutationCount、nextDelayMs 可用于展示保存与重试状态。移除编辑器时释放事件订阅。
合并发送延迟与确认超时是两个配置。客户端插件默认使用 sendChangesetTimeout: 2000、changesetAckTimeout: 20000、maxChangesetAckTimeout: 120000,单位均为毫秒。提交请求也使用确认超时;未收到确认时按指数退避重发,间隔不超过最大值。将后两项设为相同值可使用固定间隔。
import { UniverCollaborationClientPlugin } from '@univerjs-pro/collaboration-client'import { BrowserCollaborationSocketService } from '@univerjs-pro/collaboration-client-ui'univer.registerPlugin(UniverCollaborationClientPlugin, { socketService: BrowserCollaborationSocketService, sendChangesetTimeout: 200, changesetAckTimeout: 20_000, maxChangesetAckTimeout: 120_000,})将这些选项合并到现有客户端注册中,保留 socket 与接口地址配置。Sheets 和 Docs 协同预设使用 200 ms 的合并发送延迟,未暴露确认超时选项;需要自定义超时时使用插件模式。
由应用处理冲突
文档进入冲突后,CollaborationConflict 在尝试清理离线缓存后触发一次,请在加载文档前监听。reason 为 transform_failed、apply_failed、changeset_rejected、permission_rejected 或 remote_revert;可用时由 message 和 revert 提供更多信息。
import type { IEventParamConfig } from '@univerjs/core/facade'import '@univerjs-pro/collaboration-client/facade'const conflicts = new Map<string, IEventParamConfig['CollaborationConflict']>()const conflictSubscription = univerAPI.addEvent(univerAPI.Event.CollaborationConflict, (event) => { conflicts.set(event.unitId, event)})上述示例保留事件,供应用恢复界面使用。Map 仅存在于内存:刷新前应由应用保存或导出 localChanges。需在事件中留存这些数据,后续读取无法恢复已经清理的缓存。awaitingChangeset 可能已被服务端应用,不应直接重新提交。
cacheCleared: false 表示刷新会重放旧缓存,应先解决缓存清理问题。清理缓存不代表未同步修改已保存;permission_rejected 还需要解决权限问题,刷新不会授予权限。
默认 UI 按冲突原因显示通知,缓存清理成功后提供刷新操作,清理失败时提供重试。若由应用接管恢复流程,在现有初始化中设置 enableDefaultConflictUI: false,并自行处理事件:
import { UniverCollaborationClientUIPlugin } from '@univerjs-pro/collaboration-client-ui'univer.registerPlugin(UniverCollaborationClientUIPlugin, { enableDefaultConflictUI: false })import { UniverSheetsCollaborationPreset } from '@univerjs/preset-sheets-collaboration'const collaborationPreset = UniverSheetsCollaborationPreset({ enableDefaultConflictUI: false })import { UniverDocsCollaborationPreset } from '@univerjs/preset-docs-collaboration'const collaborationPreset = UniverDocsCollaborationPreset({ enableDefaultConflictUI: false })将协同预设放入现有 createUniver({ presets: [...] }),与编辑器预设组合,保留接口地址及历史记录配置。详见事件参数和客户端配置。
你觉得这篇文档如何?