共同編集の統合
Univer エディターを共同編集サービスに接続し、同じドキュメントを複数のユーザーで編集します。
共同編集はブラウザーのエディターを共有ドキュメントサービスに接続します。例えば、2 人が同じスプレッドシートを開くと、一方のブラウザーでの編集が他方にも表示されます。
サーバー統合に従ってバックエンドを準備し、以下の手順でエディターと読み込みを設定します。共同編集クイックスタートの Server と Web のコードを対にして確認してください。
既存エディターへの接続
既存の Web SDK エディターはそのまま使います。クイックスタートのブラウザー設定に従い、共同編集プラグインと、読み込み・変更送信・WebSocket のアドレスを設定します。ブラウザーとサーバーのパッケージバージョンに互換性を持たせてください。
サンプルの固定ユーザーとデフォルトの権限はデモ用です。実際のリクエストにはログイン資格情報を含め、バックエンドで ID とドキュメントへのアクセスを検証してください。ライセンスについてはライセンス設定を参照してください。
同じドキュメントの作成と表示
作成と表示は別の操作です。
- ユーザーがドキュメントを作成すると、バックエンドが保存して 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 を参照してください。対象製品とバックエンドのリリースでの対応を確認します。
同期、保存、権限の検証
通常のウィンドウとプライベートウィンドウなど、独立した 2 つのセッションで同じドキュメントを開きます。一方で編集して他方を確認し、再読み込み、再接続、サーバー再起動後も内容を確認します。
編集者、閲覧者、権限のないユーザーでテストしてください。読み取り専用 UI はページ操作を制御しますが、サーバーでも未許可の読み取りや変更を拒否する必要があります。
読み込みはできても編集が同期されない場合は WebSocket 接続を確認します。再起動で内容が消える場合はバックエンドのストレージを確認します。設定はサーバー統合を参照してください。
ファイル交換、コメント、履歴の追加
取り込んだファイルを共同編集するには、変換後のデータから共同編集ドキュメントを作成します。ファイル交換の統合を参照してください。コメントと履歴には専用のプラグイン、サービス、権限が必要です。Office 拡張モジュールに従ってください。
履歴パネルの設定は Sheets、Docs、Slides、Boards、Bases を参照してください。PDFs の現在の対応範囲は PDFs の履歴に記載されています。
同期状態と再送
共同編集 Facade をインポートして監視します。CollaborationStatusChanged は登録時に共同編集中の文書の現在の状態を通知し、その後は連続する同一状態を重複通知しません。hasWritePending(unitId) は送信待ち、確認待ち、オフラインの編集を含みます。ID を省略するとフォーカス中の文書を確認します。接続済みであることは保存完了を意味しません。
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、request_failed、license_editing_connections_exceeded、should_retry、reconnect です。attempt、elapsedMs、unsyncedMutationCount、nextDelayMs を保存・再試行の表示に利用できます。エディターを破棄する際は購読を解除してください。
request_failed は、ネットワークエラーや HTTP 408、429、5xx など、再試行可能なリクエストの失敗を示します。license_editing_connections_exceeded は、ライセンスで許可された編集接続数を使い切り、サーバーが再送を要求したことを示します。
まとめて送信する遅延と確認タイムアウトは別です。プラグインの既定値は sendChangesetTimeout: 2000、changesetAckTimeout: 20000、maxChangesetAckTimeout: 120000(ミリ秒)です。送信リクエストにも確認タイムアウトが適用され、未確認の変更は上限まで指数バックオフで再送します。後ろの 2 項目を同じ値にすると固定間隔になります。
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、license_unit_type_rejected、permission_rejected、remote_revert のいずれかで、必要に応じて message と revert が詳細を示します。
license_unit_type_rejected は、サーバーのライセンスがこの文書タイプの共同編集に対応していないことを示します。
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: [...] }) にエディタープリセットと組み合わせ、エンドポイントと履歴設定を維持してください。イベント引数とクライアント設定も参照してください。
サーバーの拒否理由とライセンス通知
サーバー拒否時の CollaborationConflict.message には、サーバーが提供した理由が入ります。復旧処理は翻訳済みメッセージを解析せず reason で分岐してください。license_unit_type_rejected では対象文書を許可するサーバーライセンスが必要です。license_editing_connections_exceeded は再送通知であり、変更は未確認のまま再試行されます。hasWritePending(unitId) が false になるか変更を別途保存するまで、エディターを開いておいてください。
既定 UI は編集接続数の上限を通知し、初回送信から 20 秒以上経過した再送では保存タイムアウトも通知します。文書が確認待ち状態を離れるか破棄されると通知は消えます。ドメイン拒否にも専用通知があります。enableDefaultConflictUI: false は競合の復旧通知だけを無効にし、これらの待機・ドメイン通知は無効にしません。
このガイドはいかがですか?