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 SheetsZhCN from '@univerjs/sheets/locale/zh-CN'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_CN,  locales: {    [LocaleType.ZH_CN]: mergeLocales(      SheetsZhCN,    ),  },})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 here are part of packages/facade/src/apis/everything.ts// However some plugins are implemented incorrectly, so they are not included here.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_CN,    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: {      // Example: If your Univer server requires authentication via cookies, you can set the 'cookie' header here.      cookie: 'your-cookie',      // ... more headers    },  })  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.