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

使用

TypeScript
import { LocaleType, 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 '@univerjs/docs/facade'const univer = new Univer({  locale: LocaleType.ZH_CN,  locales: {    [LocaleType.ZH_CN]: {},  },})univer.registerPlugin(UniverRenderEnginePlugin)univer.registerPlugin(UniverFormulaEnginePlugin)univer.registerPlugin(UniverDocsPlugin)const univerAPI = FUniver.newAPI(univer)univerAPI.createDocument({})

协同在 Node.js 环境下的使用示例

Node.js 作为协同客户端加载服务端已有的文档,通过 Facade API 修改内容,并将修改同步到打开同一文档的其他客户端。运行前,请准备好协同服务、客户端许可证和可访问的文档 ID。

示例文件

按以下结构组织文件:

text
src/docs/node/├── main.ts└── univer.ts
  • univer.ts:创建 Univer,注册文档功能插件、Facade 扩展和 Node.js 协同客户端。
  • main.ts:加载协同文档,依次演示段落、文本、页眉页脚、提示块、代码块、引用块、列表、形状、表格和分栏 API。

替换 univer.ts 中的 your-license、服务地址和 Cookie,以及 main.ts 中的文档 ID。

TypeScript
import { UniverCollaborationPlugin } from '@univerjs-pro/collaboration'import { UniverCollaborationClientPlugin } from '@univerjs-pro/collaboration-client'import {  NodeCollaborationSocketService,  UniverCollaborationClientNodePlugin,} from '@univerjs-pro/collaboration-client-node'import { UniverDocsCalloutPlugin } from '@univerjs-pro/docs-callout'import { UniverDocsCodePlugin } from '@univerjs-pro/docs-code'import { UniverDocsColumnPlugin } from '@univerjs-pro/docs-column'import { UniverDocsLatexPlugin } from '@univerjs-pro/docs-latex'import { UniverDocsListPlugin } from '@univerjs-pro/docs-list'import { UniverDocsQuotePlugin } from '@univerjs-pro/docs-quote'import { UniverDocsReferencePlugin } from '@univerjs-pro/docs-reference'import { UniverDocsShapePlugin } from '@univerjs-pro/docs-shape'import { UniverDocsTablePlugin } from '@univerjs-pro/docs-table'import { UniverLicensePlugin } from '@univerjs-pro/license'import {  IAuthzIoService,  IImageIoService,  IMentionIOService,  IUndoRedoService,  LocaleType,  Univer,} from '@univerjs/core'import { UniverDocsPlugin } from '@univerjs/docs'import { UniverDocsDrawingPlugin } from '@univerjs/docs-drawing'import { UniverDocsHyperLinkPlugin } from '@univerjs/docs-hyper-link'import { UniverDrawingPlugin } from '@univerjs/drawing'import { UniverRenderEnginePlugin } from '@univerjs/engine-render'import { UniverNetworkPlugin } from '@univerjs/network'import { UniverThreadCommentPlugin } from '@univerjs/thread-comment'import '@univerjs/docs/facade'import '@univerjs/docs-drawing/facade'import '@univerjs-pro/docs-shape/facade'import '@univerjs-pro/docs-callout/facade'import '@univerjs-pro/docs-reference/facade'import '@univerjs-pro/docs-code/facade'import '@univerjs-pro/docs-list/facade'import '@univerjs-pro/docs-quote/facade'import '@univerjs-pro/docs-table/facade'import '@univerjs-pro/docs-column/facade'import '@univerjs-pro/docs-latex/facade'import '@univerjs-pro/collaboration-client/facade'export function createUniverOnNode(): Univer {  const univer = new Univer({    locale: LocaleType.ZH_CN,    locales: {},    override: [      [IAuthzIoService, null],      [IUndoRedoService, null],      [IMentionIOService, null],    ],  })  univer.registerPlugin(UniverLicensePlugin, {    license: 'your-license',  })  univer.registerPlugin(UniverRenderEnginePlugin)  univer.registerPlugin(UniverThreadCommentPlugin)  univer.registerPlugin(UniverDrawingPlugin, {    override: [[IImageIoService, null]],  })  univer.registerPlugin(UniverDocsPlugin)  univer.registerPlugin(UniverDocsDrawingPlugin)  univer.registerPlugin(UniverDocsHyperLinkPlugin)  univer.registerPlugin(UniverDocsReferencePlugin)  univer.registerPlugin(UniverDocsCalloutPlugin)  univer.registerPlugin(UniverDocsCodePlugin)  univer.registerPlugin(UniverDocsListPlugin)  univer.registerPlugin(UniverDocsQuotePlugin)  univer.registerPlugin(UniverDocsShapePlugin)  univer.registerPlugin(UniverDocsTablePlugin)  univer.registerPlugin(UniverDocsColumnPlugin)  univer.registerPlugin(UniverDocsLatexPlugin)  registerCollaborationPlugins(univer)  return univer}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)}

运行说明

loadDocAsync() 加载服务端已有文档,无需再调用 createDocument()。协同服务通过 customHeaders 携带的 Cookie 等认证信息识别用户并检查权限,连接的 memberID 由服务端返回。

本例未启用浏览器离线缓存和多标签页实例锁,无需为了缓存账号隔离调用 setCurrentUser()。如果后续使用评论等依赖本地当前用户的功能,可以再通过 UserManagerService 设置业务用户;这不会替代服务端认证。

页眉页脚示例仅适用于分页文档,分栏示例仅适用于现代文档。示例通过 isModern() 判断文档模式,并跳过不适用的操作。

示例会向已有文档插入内容,建议使用测试文档运行。运行后保持协同连接,可在浏览器打开同一文档查看修改。flush() 等待已排队的协同修改得到服务端确认;如果改成一次性脚本,应在所有异步操作和同步完成后调用 univer.dispose() 释放实例。

你觉得这篇文档如何?

© 2026 DreamNum Co., Ltd.