Intégration collaborative

Connecter l’éditeur Univer à un service collaboratif pour modifier un même document à plusieurs.

La collaboration connecte les éditeurs du navigateur à un service de documents partagés. Par exemple, deux utilisateurs ouvrent le même classeur et une modification dans un navigateur apparaît dans l’autre.

Préparez le backend avec l’intégration serveur, puis configurez l’éditeur et le chargement ci-dessous. Suivez ensemble les codes Server et Web du Démarrage rapide de la collaboration.

100%

Connecter votre éditeur existant

Conservez votre éditeur Web SDK. Suivez la configuration navigateur du démarrage rapide pour ajouter les plugins collaboratifs et configurer le chargement, l’envoi de modifications et les adresses WebSocket. Gardez les versions des paquets navigateur et serveur compatibles.

Les utilisateurs fixes et les autorisations par défaut des exemples servent à la démonstration. Dans votre application, joignez les identifiants de connexion aux requêtes pour que le backend vérifie l’identité et l’accès au document. Consultez la configuration de la licence pour la licence.

Créer et ouvrir le même document

La création et l’ouverture sont deux actions distinctes :

  1. Lorsqu’un utilisateur crée un document, votre backend l’enregistre et renvoie son ID.
  2. Le navigateur charge cet ID. Réutilisez-le pour rouvrir le document ou le partager.

Le unitId de l’API est cet ID de document. Ne créez pas un nouveau document local vierge à chaque visite.

Définissez l’utilisateur courant avant de charger un document. Il identifie le collaborateur et possède le cache hors ligne local ; voir Identité et autorisations.

Par exemple, après avoir enregistré les plugins Sheets et collaboratifs, chargez un classeur du serveur :

TypeScript
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')

Consultez les guides Sheets, Docs, Slides, Boards, Bases et PDFs pour le chargement propre à chaque produit. Vérifiez la prise en charge par le produit et la version backend ciblée.

Vérifier synchronisation, enregistrement et autorisations

Ouvrez le même document dans deux sessions indépendantes, par exemple une fenêtre normale et une privée. Modifiez dans l’une et vérifiez l’autre. Vérifiez ensuite le contenu après actualisation, reconnexion et redémarrage serveur.

Testez avec un éditeur, un lecteur et un utilisateur non autorisé. Les réglages UI de lecture seule contrôlent les interactions ; le serveur doit toujours refuser les lectures et changements non autorisés.

Si les documents se chargent mais que les modifications ne se synchronisent pas, vérifiez WebSocket. Si le contenu disparaît au redémarrage, vérifiez le stockage backend. Ces réglages sont décrits dans l’intégration serveur.

Ajouter échange de fichiers, commentaires ou historique

Pour modifier ensemble un fichier importé, créez un document collaboratif à partir des données converties ; consultez l’intégration de l’échange de fichiers. Commentaires et historique nécessitent leurs propres plugins, services et autorisations. Suivez les modules d’extension Office.

Pour les panneaux d’historique, consultez Sheets, Docs, Slides, Boards et Bases. La portée actuelle pour PDFs est décrite dans Historique PDFs.

État de synchronisation et renvois

Après import du Facade, CollaborationStatusChanged signale l’état courant des documents déjà collaboratifs lors de l’abonnement, puis les changements sans doublons consécutifs. hasWritePending(unitId) inclut les modifications en attente d’envoi, d’accusé de réception et hors ligne. Sans ID, il vérifie le document ciblé. Une connexion ouverte ne signifie pas que tout est enregistré.

TypeScript
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 intervient uniquement lors des renvois. Les raisons sont ack_timeout, request_failed, license_editing_connections_exceeded, should_retry et reconnect. attempt, elapsedMs, unsyncedMutationCount et nextDelayMs permettent d’afficher l’enregistrement et les tentatives. Libérez les abonnements à la suppression de l’éditeur.

request_failed indique un échec de requête permettant une nouvelle tentative, comme une erreur réseau ou HTTP 408, 429 ou 5xx. license_editing_connections_exceeded indique que toutes les connexions d’édition autorisées par la licence sont utilisées et que le serveur a demandé un renvoi.

Le délai de regroupement diffère du délai d’accusé de réception. Valeurs du plugin en millisecondes : sendChangesetTimeout: 2000, changesetAckTimeout: 20000, maxChangesetAckTimeout: 120000. La requête d’envoi utilise aussi le délai d’accusé. Sans confirmation, les renvois suivent un recul exponentiel jusqu’au maximum. Deux délais d’accusé identiques donnent un intervalle constant.

TypeScript
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,})

Ajoutez ces options à l’enregistrement existant en conservant socket et endpoints. Les presets collaboratifs Sheets et Docs utilisent un regroupement de 200 ms et n’exposent pas les délais d’accusé ; utilisez le mode plugin pour les personnaliser.

Gérer les conflits dans l’application

CollaborationConflict intervient une fois à l’entrée en conflit, après la tentative de nettoyage du cache hors ligne. Abonnez-vous avant le chargement. reason vaut transform_failed, apply_failed, changeset_rejected, license_unit_type_rejected, permission_rejected ou remote_revert ; message et revert apportent des détails si disponibles.

license_unit_type_rejected indique que la licence du serveur ne couvre pas la collaboration pour ce type de document.

TypeScript
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)})

L’exemple conserve l’événement pour un écran de récupération. La Map reste en mémoire : avant de recharger, enregistrez ou exportez localChanges dans l’application. C’est lors de cet événement qu’il faut les retenir ; le cache effacé n’est plus récupérable ensuite. awaitingChangeset a peut-être déjà été appliqué sur le serveur : ne le rejouez pas aveuglément.

Avec cacheCleared: false, recharger rejouerait les anciennes modifications du cache. Résolvez le nettoyage avant de recharger. Effacer le cache ne confirme pas la sauvegarde des modifications. permission_rejected exige aussi de résoudre l’accès ; recharger ne donne aucun droit.

Par défaut, l’UI affiche une notification selon la raison et propose de recharger après nettoyage réussi, ou de réessayer si le nettoyage échoue. Pour gérer la récupération vous-même, définissez enableDefaultConflictUI: false dans l’initialisation existante et traitez l’événement :

TypeScript
import { UniverCollaborationClientUIPlugin } from '@univerjs-pro/collaboration-client-ui'univer.registerPlugin(UniverCollaborationClientUIPlugin, { enableDefaultConflictUI: false })

Ajoutez le preset collaboratif à createUniver({ presets: [...] }) avec celui de l’éditeur, en conservant les endpoints et l’historique. Voir les paramètres d’événement et la configuration client.

Motifs serveur et notifications de licence

En cas de rejet, CollaborationConflict.message contient le motif fourni par le serveur. Décidez de la récupération via reason, sans analyser le message traduit. license_unit_type_rejected nécessite une licence serveur couvrant ce type de document ; license_editing_connections_exceeded est un signal de renvoi : les modifications restent non confirmées et sont réessayées. Gardez l’éditeur ouvert jusqu’à ce que hasWritePending(unitId) soit false ou que l’application ait conservé les modifications.

L’UI signale la capacité d’édition épuisée et, lors d’un renvoi au moins 20 secondes après le premier envoi, un délai de sauvegarde dépassé. Ces notifications disparaissent lorsque le document quitte l’attente de confirmation ou est détruit. Le rejet du domaine dispose aussi d’une notification. enableDefaultConflictUI: false désactive uniquement les notifications de récupération des conflits, pas celles d’attente ou de domaine.

Que pensez-vous de ce guide ?

© 2026 DreamNum Co., Ltd.