Headless Univer

Headless Univer 是 Univer 的無介面內容執行環境,與瀏覽器編輯器共用內容模型、命令、計算和 Facade API。它按需組合外掛,不掛載互動式編輯器 UI,可在 Node.js 中處理內容,也可用於瀏覽器中的無介面任務。可用能力由註冊的產品與功能外掛決定。

在 Node.js 中可建立或載入活頁簿、讀寫儲存格與格式、調整列欄、執行公式並儲存 Univer 快照。篩選、排序、資料驗證、樞紐分析與圖表需對應內容外掛;修改圖表資料不等於渲染圖表。

以下使用外掛模式。@univerjs/core 與產品內容外掛屬於 Headless 內容層;@univerjs/ui、產品 *-ui 套件和瀏覽器核心預設組合不屬於此設定。範例保留 @univerjs/engine-render 作為共用相依項目,不掛載 Canvas 或編輯器。分類與例外見 Headless 套件邊界。

本機快照操作不需協同服務。協同範例另需用戶端、伺服器與身分驗證;Office 轉換和截圖分別需 Server SDK 與渲染執行環境。僅匯入 /facade 不會註冊功能外掛,也不會讓 UI API 變成 Headless API。

安裝

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 SheetsZhTW from '@univerjs/sheets/locale/zh-TW'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.ZH_TW,  locales: {    [LocaleType.ZH_TW]: mergeLocales(      SheetsZhTW,    ),  },})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 替換為同一份授權,並替換伺服器位址、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.ZH_TW,    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 的設定區分三種模式:主程序啟用 worker 時設定 true,worker 中設定 false,不啟用 worker 時省略或設定為 undefined,在目前程序同時註冊樞紐分析表服務和計算邏輯。因此主程序使用 useComputingWorker ? true : undefined,不能直接傳入 useComputingWorker。

此處的 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.