# 協同整合

> 將 Univer 編輯器接入協同服務，讓使用者編輯同一份文件。

- Human documentation: [https://docs.univer.ai/zh-TW/server/collaboration/browser-integration](https://docs.univer.ai/zh-TW/server/collaboration/browser-integration)

- Agent Markdown: [https://docs.univer.ai/zh-TW/server/collaboration/browser-integration.md](https://docs.univer.ai/zh-TW/server/collaboration/browser-integration.md)

- Requested language: `zh-TW`

- Content language: `zh-TW`

- Documentation version: `1.2.0`

- Source: [collaboration/browser-integration.zh-TW.mdx](https://github.com/dream-num/documentation/blob/dev/content/server/collaboration/browser-integration.zh-TW.mdx)

---

協同功能將瀏覽器編輯器連接到共用文件服務。例如，兩位使用者開啟同一份試算表，一個瀏覽器中的修改會出現在另一個瀏覽器。

先依[伺服器整合](https://docs.univer.ai/zh-TW/server.md)準備後端，再按照下方內容設定編輯器與文件載入。請對照 [協同快速開始](https://docs.univer.ai/zh-TW/server/collaboration/quick-start.md)成對的 Server 與 Web 程式碼。

```mermaid
flowchart LR; A["使用者 A 編輯器"] <-->|交換修改| S["協同服務"]; S <-->|交換修改| B["使用者 B 編輯器"]; S --> D[("儲存文件")];
```

## 接入現有編輯器

保留現有 Web SDK 編輯器，依[快速開始的瀏覽器設定](https://docs.univer.ai/zh-TW/server/collaboration/quick-start.md)加入協同外掛，設定文件載入、修改提交與 WebSocket 位址。保持瀏覽器與伺服器套件版本相容。

範例中的固定使用者與預設權限只用於示範。實際應用程式的請求應包含登入憑證，讓後端驗證身分與文件存取權。授權相關設定請參閱[授權設定](https://docs.univer.ai/zh-TW/server/license.md)。

## 建立並開啟同一份文件

建立與開啟是兩個不同操作：

1. 使用者建立文件時，後端儲存文件並回傳 ID。
2. 瀏覽器載入此 ID；重新開啟或與其他使用者分享時，都重用相同 ID。

API 中的 `unitId` 就是這個文件 ID。不要每次造訪都建立新的本機空白文件。

先設定目前使用者，再載入文件。目前使用者決定協同編輯的身分與本機離線快取的歸屬，設定方式請參閱[身分與授權](https://docs.univer.ai/zh-TW/server/collaboration/identity-and-authorization.md)。

例如，註冊 Sheets 與協同外掛後，可載入伺服器試算表：

```ts
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](https://docs.univer.ai/zh-TW/guides/sheets/features/collaboration.md)、[Docs](https://docs.univer.ai/zh-TW/guides/docs/features/collaboration.md)、[Slides](https://docs.univer.ai/zh-TW/guides/slides/features/collaboration.md)、[Boards](https://docs.univer.ai/zh-TW/guides/boards/features/collaboration.md)、[Bases](https://docs.univer.ai/zh-TW/guides/bases/features/collaboration.md)與 [PDFs](https://docs.univer.ai/zh-TW/guides/pdfs/features/collaboration.md) 指南。請確認產品與目標後端版本的支援情況。

## 驗證同步、儲存與權限

在兩個獨立瀏覽器工作階段中開啟相同文件，例如一般視窗與無痕視窗。在一個視窗編輯，確認另一個收到變更，再驗證重新整理、重新連線與重啟伺服器後的內容。

使用編輯者、檢視者與未授權使用者測試。唯讀 UI 設定控制頁面互動，伺服器仍須拒絕未授權的讀取與修改。

若可載入但不同步，請檢查 WebSocket 連線。若重啟後內容遺失，請檢查後端儲存。相關設定請參閱[伺服器整合](https://docs.univer.ai/zh-TW/server.md)。

## 加入檔案交換、留言或歷史記錄

若要共同編輯匯入的檔案，請使用轉換後的資料建立協同文件，詳見[檔案交換整合](https://docs.univer.ai/zh-TW/server/import-export.md)。留言與歷史記錄各自需要外掛、服務與權限，請參考 [Office 擴充模組](https://docs.univer.ai/zh-TW/server/collaboration/extensions.md)。

歷史面板設定請參閱 [Sheets](https://docs.univer.ai/zh-TW/guides/sheets/features/edit-history.md)、[Docs](https://docs.univer.ai/zh-TW/guides/docs/features/edit-history.md)、[Slides](https://docs.univer.ai/zh-TW/guides/slides/features/edit-history.md)、[Boards](https://docs.univer.ai/zh-TW/guides/boards/features/edit-history.md)、[Bases](https://docs.univer.ai/zh-TW/guides/bases/features/edit-history.md)。PDFs 目前的能力範圍請參閱 [PDFs 歷史記錄](https://docs.univer.ai/zh-TW/guides/pdfs/features/edit-history.md)。

## 同步狀態與重送

匯入協同 Facade 後註冊監聽。`CollaborationStatusChanged` 會報告註冊監聽時已在協同的文件的目前狀態，此後只報告變更，不連續重複相同狀態。`hasWritePending(unitId)` 包含等待傳送、等待確認和離線快取的修改；省略 ID 時檢查目前聚焦文件。WebSocket 已連線不代表修改已儲存。

```ts
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`，單位均為毫秒。提交請求也使用確認逾時；未收到確認時以指數退避重送，間隔不超過最大值。後兩項相同時使用固定間隔。

```ts
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` 提供更多資訊。

```ts
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`，並自行處理事件：

#### 外掛模式

```ts
import { UniverCollaborationClientUIPlugin } from '@univerjs-pro/collaboration-client-ui'

univer.registerPlugin(UniverCollaborationClientUIPlugin, { enableDefaultConflictUI: false })
```

#### Sheets 預設

```ts
import { UniverSheetsCollaborationPreset } from '@univerjs/preset-sheets-collaboration'

const collaborationPreset = UniverSheetsCollaborationPreset({ enableDefaultConflictUI: false })
```

#### Docs 預設

```ts
import { UniverDocsCollaborationPreset } from '@univerjs/preset-docs-collaboration'

const collaborationPreset = UniverDocsCollaborationPreset({ enableDefaultConflictUI: false })
```

將協同預設放入現有 `createUniver({ presets: [...] })`，與編輯器預設組合，保留介面位址與歷史設定。詳見[事件參數](https://docs.univer.ai/zh-TW/reference/facade/events.md#collaborationconflict)和[用戶端設定](https://docs.univer.ai/zh-TW/reference/packages/plugins/univerjs-pro/collaboration-client.md)。
