Headless Univer

Headless Univer は、対話型エディター UI を持たない Univer の内容ランタイムです。ブラウザー版と内容モデル、コマンド、計算、Facade API を共有します。Node.js での内容処理やブラウザー内の UI を使わない処理に利用でき、登録する製品・機能プラグインによって利用可能な機能が決まります。

Node.js でブックを作成・読み込みし、セルや書式、行列を編集し、数式を計算して Univer スナップショットを保存できます。フィルター、並べ替え、入力規則、ピボット、グラフには対応する内容プラグインが必要です。グラフデータの変更と描画は別です。

以下はプラグインモードを使います。@univerjs/core と製品の内容プラグインは Headless 層に含まれますが、@univerjs/ui、製品の *-ui、標準ブラウザープリセットは含めません。@univerjs/engine-render は共有依存として登録し、Canvas やエディターはマウントしません。詳細はパッケージの境界を参照してください。

ローカルスナップショット処理に共同編集は不要です。共同編集サンプルにはクライアント、サーバー、認証が必要です。Office 変換と画像出力にはそれぞれ Server SDK と描画ランタイムを接続します。/facade の読み込みだけでは機能プラグインの登録や UI API の Headless 化は行われません。

インストール

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

使い方

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 SheetsJaJP from '@univerjs/sheets/locale/ja-JP'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.JA_JP,  locales: {    [LocaleType.JA_JP]: mergeLocales(      SheetsJaJP,    ),  },})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({})

Node.js 環境での共同編集の例

Node.js を共同編集クライアントとして使用し、サーバー上の既存のワークブックを読み込み、Facade API で内容を変更して、同じドキュメントを開いている他のクライアントに同期できます。実行前に、共同編集サービス、クライアントライセンス、アクセス可能なドキュメント ID を用意してください。

サンプルファイル

次の構成でファイルを配置します。

text
src/sheets/node/├── main.ts├── univer.ts└── worker.ts
  • univer.ts:Univer を作成し、各機能のプラグイン、共同編集クライアント、必要に応じて RPC メインプロセスプラグインを登録します。
  • worker.ts:計算側の数式エンジン、リモート数式、フィルター、ピボットテーブルの各プラグインを登録し、RPC でメインプロセスと通信します。
  • main.ts:共同編集ドキュメントを読み込み、権限、基本的なシート操作、条件付き書式、データ入力規則、テーブル、ピボットテーブル、スパークライン、画像、グラフ、図形、グループ化、ハイパーリンクの API を順に示します。

両方のファイルの your-license を同じライセンスに置き換え、サーバー URL、Cookie、ドキュメント ID、ユーザー ID も実際の値に変更してください。main.ts の権限の例では共同編集者と保護範囲も設定するため、実際の業務ユーザーと、必要な管理権限を持つ認証情報を使用してください。

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'// ここで使用する Facade API は packages/facade/src/apis/everything.ts に含まれます// 一部のプラグインは実装が不完全なため、その API はここに含まれていません。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.JA_JP,    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 {  // ビルド成果物の実際の場所に合わせて worker のパスを設定します。  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: {      // 例:Univer サーバーが Cookie 認証を必要とする場合、ここで 'cookie' ヘッダーを設定できます。      cookie: 'your-cookie',      // ... その他のヘッダー    },  })  univer.registerPlugin(UniverCollaborationClientNodePlugin)}

計算 worker を有効にする

createUniverOnNode() の既定値は useComputingWorker: false で、現在のプロセスで数式を計算します。上記の main.ts は useComputingWorker: true を明示的に渡し、計算 worker を有効にしています。

  1. メインプロセスの UniverProFormulaEnginePlugin、UniverSheetsPlugin、UniverSheetsFormulaPlugin、UniverSheetsPivotTablePlugin にすべて notExecuteFormula: true を設定し、RPC を通じて子プロセスに計算を委譲します。
  2. UniverRPCNodeMainPlugin が workerSrc で指定されたビルド済みの worker.js を起動します。
  3. 子プロセスは UniverRPCNodeWorkerPlugin と UniverRemoteSheetsFormulaPlugin を通じてデータを受信し、計算します。

UniverSheetsPivotTablePlugin の設定には 3 つのモードがあります。worker を使うメインプロセスでは true、worker 内では false を設定します。worker を使わない場合は省略するか undefined を指定し、現在のプロセスにピボットテーブルのサービスと計算処理の両方を登録します。そのため、メインプロセスでは useComputingWorker を直接渡さず、useComputingWorker ? true : undefined を使用します。

この worker は Node.js の child_process.fork() で起動します。プロジェクトで使用しているビルドツールで main.ts と worker.ts を別々のエントリーポイントとしてビルドし、workerSrc をビルド後の worker ファイルに設定してください。サンプルでは worker.js がメインエントリーの出力と同じディレクトリにあることを想定しています。パス、拡張子、モジュール形式は実際の出力に合わせてください。ファイルの場所には CommonJS の __dirname を使っているため、ESM プロジェクトでは対応するパス解決方法に変更してください。

実行時の注意点

loadSheetAsync() はサーバー上の既存ドキュメントを読み込むため、createWorkbook() を呼び出す必要はありません。共同編集サービスは customHeaders の Cookie などの認証情報からユーザーを識別し、権限を確認します。接続の memberID はサーバーから返されます。

この例ではブラウザのオフラインキャッシュと複数タブ間のインスタンスロックを無効にしているため、アカウントごとのキャッシュ分離を目的とした setCurrentUser() の呼び出しは不要です。後からコメントなどローカルの現在のユーザーに依存する機能を使用する場合は、UserManagerService で業務ユーザーを設定できます。これはサーバー認証の代わりにはなりません。

サンプルは実行後も共同編集接続を維持します。ブラウザで同じドキュメントを開くと変更を確認できます。flush() はキュー内の変更に対するサーバーの確認を待ちます。単発のスクリプトにする場合は、すべての非同期処理と同期が完了してから univer.dispose() でインスタンスを解放してください。

このガイドはいかがですか?

© 2026 DreamNum Co., Ltd.