Headless Univer

Headless Univer est le runtime de contenu Univer sans interface d’édition interactive. Il partage les modèles de contenu, commandes, calculs et API Facade avec l’éditeur web. Il permet de traiter le contenu dans Node.js ou d’exécuter des tâches sans interface dans un navigateur. Les fonctionnalités disponibles dépendent des plugins de produit et de fonctionnalité enregistrés.

Dans Node.js, créez ou chargez des classeurs, modifiez cellules, formats, lignes et colonnes, calculez les formules et sauvegardez des instantanés Univer. Filtres, tri, validation, tableaux croisés et graphiques nécessitent leurs plugins de contenu. Modifier les données d’un graphique ne le dessine pas.

L’exemple utilise le mode plugin. @univerjs/core et les plugins de contenu produit appartiennent à la couche Headless ; @univerjs/ui, les packages produit *-ui et les presets web de base par défaut sont exclus. @univerjs/engine-render reste une dépendance partagée, sans Canvas ni éditeur monté. Voir les limites des packages Headless.

Les instantanés locaux ne nécessitent pas de collaboration. L’exemple collaboratif ajoute client, serveur et authentification. Conversion Office et captures nécessitent respectivement le Server SDK et un runtime de rendu. Importer /facade seul n’enregistre pas les plugins et ne transforme pas les API UI en API Headless.

Installation

Shell
pnpm add @univerjs/core @univerjs/engine-render @univerjs/engine-formula @univerjs/docs @univerjs/sheets @univerjs/sheets-formula @univerjs/sheets-numfmt

Utilisation

TypeScript
import { LocaleType, mergeLocales, Univer } from '@univerjs/core'import { FUniver } from '@univerjs/core/facade'import { UniverDocsPlugin } from '@univerjs/docs'import { UniverFormulaEnginePlugin } from '@univerjs/engine-formula'import { UniverRenderEnginePlugin } from '@univerjs/engine-render'import { UniverSheetsPlugin } from '@univerjs/sheets'import { UniverSheetsFormulaPlugin } from '@univerjs/sheets-formula'import { UniverSheetsNumfmtPlugin } from '@univerjs/sheets-numfmt'import SheetsFrFR from '@univerjs/sheets/locale/fr-FR'import '@univerjs/engine-formula/facade'import '@univerjs/sheets/facade'import '@univerjs/sheets-formula/facade'import '@univerjs/sheets-numfmt/facade'const univer = new Univer({  locale: LocaleType.FR_FR,  locales: {    [LocaleType.FR_FR]: mergeLocales(      SheetsFrFR,    ),  },})univer.registerPlugin(UniverRenderEnginePlugin)univer.registerPlugin(UniverFormulaEnginePlugin)univer.registerPlugin(UniverDocsPlugin)univer.registerPlugin(UniverSheetsPlugin)univer.registerPlugin(UniverSheetsFormulaPlugin)univer.registerPlugin(UniverSheetsNumfmtPlugin)const univerAPI = FUniver.newAPI(univer)univerAPI.createWorkbook({})

Exemple de collaboration dans Node.js

Node.js peut servir de client de collaboration : charger un classeur existant depuis le serveur, modifier son contenu avec l’API Facade et synchroniser les changements avec les autres clients ayant ouvert le même document. Avant l’exécution, préparez un service de collaboration, une licence client et un ID de document accessible.

Fichiers de l’exemple

Organisez les fichiers comme suit :

text
src/sheets/node/├── main.ts├── univer.ts└── worker.ts
  • univer.ts : crée Univer et enregistre les plugins fonctionnels, le client de collaboration et, si nécessaire, le plugin RPC du processus principal.
  • worker.ts : enregistre le moteur de formules côté calcul, ainsi que les plugins de formules distantes, de filtres et de tableaux croisés dynamiques, puis communique avec le processus principal par RPC.
  • main.ts : charge le document collaboratif et illustre les API de permissions, d’opérations de base sur les feuilles, de mise en forme conditionnelle, de validation des données, de tableaux, de tableaux croisés dynamiques, de mini-graphiques, d’images, de graphiques, de formes, de regroupement et de liens hypertextes.

Remplacez your-license dans les deux fichiers par la même licence, puis adaptez l’URL du serveur, le Cookie, l’ID du document et les ID des utilisateurs. L’exemple de permissions dans main.ts configure aussi les collaborateurs et les plages protégées : utilisez des utilisateurs réels de votre application et une identité disposant des droits de gestion nécessaires.

TypeScript
import { UniverCollaborationPlugin } from '@univerjs-pro/collaboration'import { UniverCollaborationClientPlugin } from '@univerjs-pro/collaboration-client'import {  NodeCollaborationSocketService,  UniverCollaborationClientNodePlugin,} from '@univerjs-pro/collaboration-client-node'import { UniverProFormulaEnginePlugin } from '@univerjs-pro/engine-formula'import { UniverLicensePlugin } from '@univerjs-pro/license'import { UniverSheetsChartPlugin } from '@univerjs-pro/sheets-chart'import { UniverSheetsOutlinePlugin } from '@univerjs-pro/sheets-outline'import { UniverSheetsPivotTablePlugin } from '@univerjs-pro/sheets-pivot'import { UniverSheetsShapePlugin } from '@univerjs-pro/sheets-shape'import { UniverSheetSparklinePlugin } from '@univerjs-pro/sheets-sparkline'import { IAuthzIoService, IImageIoService, IUndoRedoService, LocaleType, Univer } from '@univerjs/core'import { UniverDataValidationPlugin } from '@univerjs/data-validation'import { UniverDocsPlugin } from '@univerjs/docs'import { UniverDocsDrawingPlugin } from '@univerjs/docs-drawing'import { UniverDrawingPlugin } from '@univerjs/drawing'import { UniverRenderEnginePlugin } from '@univerjs/engine-render'import { UniverNetworkPlugin } from '@univerjs/network'import { UniverRPCNodeMainPlugin } from '@univerjs/rpc-node'import { UniverSheetsPlugin } from '@univerjs/sheets'import { UniverSheetsConditionalFormattingPlugin } from '@univerjs/sheets-conditional-formatting'import { UniverSheetsDataValidationPlugin } from '@univerjs/sheets-data-validation'import { UniverSheetsDrawingPlugin } from '@univerjs/sheets-drawing'import { UniverSheetsFilterPlugin } from '@univerjs/sheets-filter'import { UniverSheetsFormulaPlugin } from '@univerjs/sheets-formula'import { UniverSheetsHyperLinkPlugin } from '@univerjs/sheets-hyper-link'import { UniverSheetsNotePlugin } from '@univerjs/sheets-note'import { UniverSheetsNumfmtPlugin } from '@univerjs/sheets-numfmt'import { UniverSheetsSortPlugin } from '@univerjs/sheets-sort'import { UniverSheetsTablePlugin } from '@univerjs/sheets-table'import { UniverThreadCommentPlugin } from '@univerjs/thread-comment'import path from 'node:path'// Les API Facade utilisées ici font partie de packages/facade/src/apis/everything.ts// Certaines implémentations de plugins sont incomplètes ; leurs API ne sont donc pas incluses ici.import '@univerjs/engine-formula/facade'import '@univerjs/sheets/facade'import '@univerjs/sheets-formula/facade'import '@univerjs/sheets-numfmt/facade'import '@univerjs/sheets-conditional-formatting/facade'import '@univerjs/sheets-data-validation/facade'import '@univerjs/sheets-filter/facade'import '@univerjs/sheets-hyper-link/facade'import '@univerjs/sheets-note/facade'import '@univerjs/sheets-table/facade'import '@univerjs/sheets-drawing/facade'import '@univerjs/sheets-thread-comment/facade'import '@univerjs-pro/engine-shape/facade'import '@univerjs-pro/sheets-pivot/facade'import '@univerjs-pro/sheets-sparkline/facade'import '@univerjs-pro/sheets-chart/facade'import '@univerjs-pro/sheets-shape/facade'import '@univerjs-pro/sheets-outline/facade'import '@univerjs-pro/collaboration-client/facade'export interface ICreateUniverOnNodeOptions {  useComputingWorker?: boolean}export function createUniverOnNode(options: ICreateUniverOnNodeOptions = {}): Univer {  const { useComputingWorker = false } = options  const univer = new Univer({    locale: LocaleType.FR_FR,    locales: {},    override: [      [IAuthzIoService, null],      [IUndoRedoService, null],    ],  })  univer.registerPlugin(UniverLicensePlugin, {    license: 'your-license',  })  registerBasicPlugins(univer, useComputingWorker)  registerSharedPlugins(univer)  if (useComputingWorker) {    registerRPCPlugin(univer)  }  registerDocPlugins(univer)  registerSheetPlugins(univer, useComputingWorker)  registerCollaborationPlugins(univer)  return univer}function registerBasicPlugins(univer: Univer, useComputingWorker: boolean): void {  univer.registerPlugin(UniverProFormulaEnginePlugin, { notExecuteFormula: useComputingWorker })}function registerSharedPlugins(univer: Univer): void {  univer.registerPlugin(UniverRenderEnginePlugin)  univer.registerPlugin(UniverThreadCommentPlugin)  univer.registerPlugin(UniverDrawingPlugin, {    override: [[IImageIoService, null]],  })}function registerDocPlugins(univer: Univer): void {  univer.registerPlugin(UniverDocsPlugin)  univer.registerPlugin(UniverDocsDrawingPlugin)}function registerSheetPlugins(univer: Univer, useComputingWorker: boolean): void {  univer.registerPlugin(UniverSheetsPlugin, { notExecuteFormula: useComputingWorker })  univer.registerPlugin(UniverSheetsFormulaPlugin, { notExecuteFormula: useComputingWorker })  univer.registerPlugin(UniverSheetsNumfmtPlugin)  univer.registerPlugin(UniverSheetsConditionalFormattingPlugin)  univer.registerPlugin(UniverDataValidationPlugin)  univer.registerPlugin(UniverSheetsDataValidationPlugin)  univer.registerPlugin(UniverSheetsFilterPlugin)  univer.registerPlugin(UniverSheetsHyperLinkPlugin)  univer.registerPlugin(UniverSheetsNotePlugin)  univer.registerPlugin(UniverSheetsDrawingPlugin)  univer.registerPlugin(UniverSheetsSortPlugin)  univer.registerPlugin(UniverSheetsTablePlugin)  univer.registerPlugin(UniverSheetsChartPlugin)  univer.registerPlugin(UniverSheetsShapePlugin)  univer.registerPlugin(UniverSheetsPivotTablePlugin, {    notExecuteFormula: useComputingWorker ? true : undefined,  })  univer.registerPlugin(UniverSheetSparklinePlugin)  univer.registerPlugin(UniverSheetsOutlinePlugin)}function registerRPCPlugin(univer: Univer): void {  // Définissez le chemin du worker selon l’emplacement réel du fichier compilé.  const childPath = path.join(__dirname, 'worker.js')  univer.registerPlugin(UniverRPCNodeMainPlugin, { workerSrc: childPath })}function registerCollaborationPlugins(univer: Univer): void {  const serverUrl = 'https://your-univer-server.example.com'  univer.registerPlugin(UniverNetworkPlugin)  univer.registerPlugin(UniverCollaborationPlugin)  univer.registerPlugin(UniverCollaborationClientPlugin, {    socketService: NodeCollaborationSocketService,    enableOfflineEditing: false,    enableSingleActiveInstanceLock: false,    snapshotServerUrl: `${serverUrl}/universer-api/snapshot`,    collabSubmitChangesetUrl: `${serverUrl}/universer-api/comb`,    collabWebSocketUrl: `${serverUrl}/universer-api/comb/connect`,    wsSessionTicketUrl: `${serverUrl}/universer-api/user/session-ticket`,    uploadFileServerUrl: `${serverUrl}/universer-api/stream/file/upload`,    signUrlServerUrl: `${serverUrl}/universer-api/file/{fileID}/sign-url`,    downloadEndpointUrl: `${serverUrl}`,    authzUrl: `${serverUrl}/universer-api/authz`,    sendChangesetTimeout: 200,    retryConnectingInterval: 1000,    customHeaders: {      // Exemple : si votre serveur Univer exige une authentification par Cookie, définissez l’en-tête 'cookie' ici.      cookie: 'your-cookie',      // ... autres en-têtes    },  })  univer.registerPlugin(UniverCollaborationClientNodePlugin)}

Activer le worker de calcul

createUniverOnNode() utilise par défaut useComputingWorker: false et calcule les formules dans le processus courant. Le fichier main.ts ci-dessus passe explicitement useComputingWorker: true pour activer le worker de calcul :

  1. Dans le processus principal, UniverProFormulaEnginePlugin, UniverSheetsPlugin, UniverSheetsFormulaPlugin et UniverSheetsPivotTablePlugin définissent tous notExecuteFormula: true pour déléguer les calculs au processus enfant par RPC.
  2. UniverRPCNodeMainPlugin démarre le fichier worker.js compilé indiqué par workerSrc.
  3. Le processus enfant reçoit les données et effectue les calculs via UniverRPCNodeWorkerPlugin et UniverRemoteSheetsFormulaPlugin.

UniverSheetsPivotTablePlugin distingue trois modes : true dans le processus principal avec un worker, false dans le worker, et une option omise ou définie à undefined sans worker pour enregistrer les services et les calculs de tableaux croisés dynamiques dans le processus courant. Le processus principal utilise donc useComputingWorker ? true : undefined au lieu de passer directement useComputingWorker.

Node.js démarre ce worker avec child_process.fork(). Utilisez votre outil de compilation habituel pour compiler main.ts et worker.ts comme deux points d’entrée distincts, puis faites pointer workerSrc vers le fichier worker compilé. L’exemple suppose que worker.js est dans le même répertoire que la sortie principale ; adaptez le chemin, l’extension et le format de module à votre compilation. L’exemple utilise __dirname de CommonJS pour localiser le fichier ; un projet ESM doit employer la méthode de résolution correspondante.

Notes d’exécution

loadSheetAsync() charge un document existant sur le serveur ; il est inutile d’appeler createWorkbook(). Le service de collaboration identifie l’utilisateur et vérifie les permissions à partir des informations d’authentification, telles que le Cookie dans customHeaders. Le serveur renvoie le memberID de la connexion.

Cet exemple désactive le cache hors ligne du navigateur et le verrou d’instance entre onglets. Il est donc inutile d’appeler setCurrentUser() pour isoler le cache entre comptes. Si vous utilisez ensuite des fonctions comme les commentaires, qui dépendent de l’utilisateur courant local, vous pouvez définir l’utilisateur de l’application avec UserManagerService ; cela ne remplace pas l’authentification du serveur.

L’exemple maintient la connexion collaborative après son exécution. Ouvrez le même document dans un navigateur pour voir les modifications. flush() attend l’accusé de réception du serveur pour les modifications en attente. Pour un script exécuté une seule fois, appelez univer.dispose() une fois toutes les opérations asynchrones et la synchronisation terminées.

Que pensez-vous de ce guide ?

© 2026 DreamNum Co., Ltd.