☰
Univer开源表格组件:从嵌入到在线协同的实践指南
2026/10/2 18:10:06 网站建设 项目流程

如果你和我一样,长期在后台管理系统、数据中台或者在线办公类产品里做前端,大概率会被一个问题反复折磨:不就是想给用户一个能编辑、能算公式、能多人同步修改的“网页版Excel”吗?性能好的不开放,开放商用的费用高,自己造轮子又会在公式引擎和协作冲突这里卡到怀疑人生。直到我看到并实际去调研了Univer这个开源项目,才发现它几乎精准地补上了这块空白。Univer 提供了一整套开源的 Web Office 基础能力,核心是电子表格(Spreadsheet),同时也包含文档和幻灯片模块,常见说法是“客厅里的谷歌 Sheets”,但它比 Sheets 更灵活——因为你可以像引入一个 SDK 那样,把它嵌进自己的业务系统里,而不是跳去另一个网站操作。这篇文章,我想从自己的体验出发,讲讲 Univer 到底能做什么、怎么跑起来、哪些地方让我踩了坑,以及它在在线协同场景里到底值不值得托付。

1. 为什么我会在 Web 表格组件上重新选型

1.1 先看传统选项的尴尬处境

在 Univer 出现之前,想在 Web 端实现一套交互接近 Excel 的表格,业界通常有几条路:简单一点用 Handsontable,表格交互很顺滑,但它是一个商业许可组件,社区版的限制在真实项目里很快会撞见;功能全面一点用 Luckysheet,它很受欢迎,但在持续维护和架构扩展上有先天负担;再重型一点就是 OnlyOffice,能力强,但部署体系偏重,而且如果你想把它作为“模块”嵌进自己的 React 或者 Vue 应用里,Strictly 说并不轻松。

这些方案的共同矛盾其实在于:要么太轻,只能做“看起来像表格”的数据录入界面,公式、条件格式、数据透视表这些硬需求完全缺失;要么太重,自成一套应用生态,难以融入公司自己的权限、接口和交互风格。

1.2 Univer 的定位恰好是“可嵌入的 Office 基础层”

Univer 在自己官网和 GitHub 上的定位很明确:开源的、可扩展的、跨端的在线协作办公套件,底层用 TypeScript 从头实现。它不是一个完整业务应用,而是一套可以嵌进你现有项目的“基础组件套件”。

我理解它的价值分三层:

  • 第一层是基础表格能力,包括单元格编辑、行列拖拽、冻结窗格、筛选、排序、图表、条件格式、数据透视表等,基本上日常办公里高频用到的功能都有;
  • 第二层是计算引擎,Univer 内置了公式引擎,支持几百个函数,还支持跨工作表引用甚至跨文件引用,这一点对做业务系统特别关键;
  • 第三层是协同能力,它提供了多人实时编辑的协议和示例,你只需要自建或者接入一个 WebSocket 服务端,就可以让你的表格像在线文档一样多人并发修改。

所以如果你正在做一个运营后台、财务系统、教育题库平台,或者任何一个需要“用户上传一张 Excel / 在线直接编辑数据表”的场景,Univer 给了你一条很体面的路径:不用自己造数据模型,不用自己实现公式解析,而是像接地图组件一样接上它。

1.3 一个简单的框架中立概念

还有一点值得肯定:Univer 本身不绑定具体的前端框架。你在 React 项目里能用,在 Vue 项目里也能用,哪怕是用原生 JS 和简单打包工具也能跑起来。它的架构把所有交互和绘制逻辑解耦成插件,UI 层通过 DOM 和 Canvas 混合渲染,宿主应用只负责提供一个挂载节点。这个设计意味着你不需要为了引入它而推翻现有技术栈,减小了项目落地的心理负担。

这对我这样的“业务集成党”来说特别友好。你要做的不是“迁移到一个在线表格产品”,而是“在自己的工程目录里安装一个 npm 包”。

2. 从 npm 到页面:Univer 的实例化与第一个表格

2.1 环境准备与最简安装

坦白说,Univer 的包名和导入方式在最近一两年经历过几次调整,不同版本的 API 有差异。如果你现在去项目里安装,一定要以官方文档当前最新版本为准,不要照抄网上 2023 年甚至 2024 年初的旧教程。我这里给出的是一个贴近当前版本、且能跑通的最简流程。

假定你是在一个 Vite + React 项目里操作:

npm install @univerjs/preset-sheets

这个预设包里已经集成了创建电子表格需要的核心插件,包括渲染引擎、交互控制器、公式计算、剪切板等。安装之后,直接在组件里初始化:

import { UniverSheet } from '@univerjs/preset-sheets' const univer = UniverSheet.create({ container: document.getElementById('sheet-container')!, locale: 'zhCN', options: { allowEditing: true, }, }) univer.createSheet({ name: 'Demo' })

不需要再手动注册一堆插件,UniverSheet.create会按照内部配置把核心部分装配好。接着你的 HTML 里只需要有一个具备宽高的容器:

<div id="sheet-container" style="width: 100vw; height: 80vh;"></div>

页面加载后,你就能看到完整的表格界面——有工具栏、行号列标、可编辑的单元格、右下角的工作表标签,基本和原生表格应用的第一屏差不多。不算加载依赖的时间,真正写业务代码可能不到二十行。

2.2 为什么不需要自己写一整套 UI

很多做过表格开发的人会习惯性地问:工具栏在哪?单元格选中态怎么处理?右键菜单怎么弹?

Univer 对这些都已经内置了,你打开页面看到的就是完整工具栏。你可以通过配置和插件来做定制,而不是从零去造轮子。比如官方示例里有隐藏工具栏、自定义单元格操作按钮、替换默认主题色的做法。基础使用阶段,建议先用内置 UI 把流程跑通,再根据业务需要逐步裁剪。

这里还有一点值得说:Univer 的界面渲染是 Canvas 核心,这意味着它不像一个个 DOM 单元格那样在节点很多时把浏览器卡死。它的渲染引擎会做区域裁剪和虚拟绘制,所以当数据量比较大的时候,体验也基本能在可控范围内。

2.3 你的第一个“在线表格”不只是出现在页面里

当 univer 实例跑起来之后,你要意识到它是可以被业务系统反过来控制和读取的。最常用的几个操作是:

// 给指定单元格写入值 univer.sheet.setCellValue({ sheetId: 'sheet-1', row: 0, col: 0, value: '你好 Univer', }) // 读取当前整个表格的数据 const data = univer.sheet.getSheetData()

这意味着你可以把 Univer 当成一个“新的数据填报交互层”:用户在前端表格里任意编辑,你通过 API 把数据拿到自己后端去存储,或者反过来从后端加载数据填进表格。这种双向数据通道才是在业务里真正使用它的关键。

3. 从“能看”到“能用”:单元格、公式与数据操作

3.1 公式引擎不是简单的字符串拼接

制作在线表格,最大的分水岭就是有没有真正的公式引擎。Univer 内置的公式系统能解析SUM(A1:B2)这种范围引用,也能处理跨表表示,它内部把公式解析成 AST,再通过依赖追踪来更新结果。

我一开始最担心的是:我们业务里会用到一些自定义的计算逻辑,比如根据考核规则加权汇总,而不是简单的 SUM。Univer 提供注册自定义函数的入口:

import { registerFunction } from '@univerjs/preset-sheets' registerFunction('MY_SCORE', (params) => { const unitScore = params[0] const coefficient = params[1] return unitScore * coefficient })

注册之后,用户在单元格里输入=MY_SCORE(B2, 0.8)就能直接得到计算结果。这一点对“以表格作为业务编辑器”的产品来说,基本上属于无法拒绝的能力——它把计算封闭在了表格层,而不是由后端一次次轮询计算。

3.2 样式、条件格式和基础交互

如果说公式是“能不能算”,那样式和条件格式就是“看起来像不像个正经表格”。在 Univer 中,单元格样式可以直接用 API 设置:

univer.sheet.setStyle({ sheetId: 'sheet-1', range: { startRow: 0, startCol: 0, endRow: 5, endCol: 5 }, style: { fontWeight: 'bold', background: '#f0f0f0', border: { top: 'thin', bottom: 'thin' }, }, })

我更常用的是在可视化界面上直接操作,因为频繁代码设置样式总归很别扭。Univer 工具栏里已经有字体、颜色、边框、合并单元格、对齐方式等基础项,不需要额外开发。条件格式同样内置了高亮重复值、数据条、色阶等规则,但需要注意:目前这些配置是保存在前端实例中的,如果你想持久化到后端,需要额外做 JSON 序列化存储,下次加载时再重新应用。

3.3 从 Excel 文件来,到 Excel 文件去

业务里几乎绕不开.xlsx文件的导入导出。Univer 的导入导出是通过插件和第三方库协作完成的,官方提供的预设包里通常包含univer/io/import和univer/io/export能力。你可以让用户上传一个 Excel,Univer 解析后把每个工作表渲染出来;也可以把编辑结果导出为.xlsx下载到本地。

这一步对你的软件体验提升是肉眼可见的——用户不用再在“excel 和 web 系统”之间反复横跳,而是先上传模板、在网页里改完、再导出回传,整套流程闭环。

3.4 数据校验:防止用户乱填的第一道防线

真正在企业应用里,数据校验比公式更刚需。Univer 提供了一个内置的数据验证功能,你可以给一个区域设置“必须为整数”“必须是下拉列表中的某个值”等约束。比如给B2:B100设置一个下拉列表:

univer.sheet.addValidation({ sheetId: 'sheet-1', range: { startRow: 1, startCol: 1, endRow: 99, endCol: 1 }, type: 'list', formula1: '"通过,不通过,待定"', })

用户在编辑时只能从这几个值里选,非法的输入会被拒绝或者提示。这个能力的价值被很多人低估,它相当于在不写一行表单校验代码的情况下,把表格本身变成了一个“数据录入表单”。

4. 在线协同的真相:Univer 的协作能力解析

4.1 “在线”两个字最容易让人低估复杂度

Univer 热词搜出来会有“univer在线”,大家希望的是跟腾讯文档、Google Sheets 一样,打开同一个文档,我在这里输入,他在那里马上看到。这件事的技术难点不在于“加一个 WebSocket 推数据”,而在于两个人的操作冲突时怎么合并。

比如 A 正在往 C3 单元格输入一段长文本,B 同时把第 3 行整体删除。这时候该听谁的?如果用简单的事件广播,一定会有人把另一个人刚输入的内容顶掉。Univer 在协同层的设计里,把用户每一次编辑抽象为可描述的操作(包括单元格值变更、行列操作、样式变更等),并利用服务端做操作转换和冲突处理,保证不同端最终能收敛到同一个状态。

幸运的是,你不需要自己逆着设计这套协议。Univer 官方提供了协同编辑的示例,示例里服务端使用 Node.js 和 WebSocket,客户端只需要通过适配器把编辑操作同步到服务端,再广播给其他在线用户。

4.2 最小协同配置:一个简单的 WebSocket 中转

这里我不展开生产级高可用方案,只说一个最便于理解的协作模型。你可以用一个简单的 Node.js websocket 服务,接收客户端发来的“操作 JSON”,然后转发给房间里的其他人:

// 伪代码,仅示意协作消息的流转 ws.on('message', (raw) => { const action = JSON.parse(raw) room.clients.forEach((client) => { if (client !== ws) client.send(JSON.stringify(action)) }) })

Univer 客户端会把操作序列和基础文档状态绑定,保证收到消息后能合并到自己的模型中。生产环境还要考虑断线重连、离线补偿、版本历史、权限冲突等,但至少验证“能不能多人操作同一张表”时,这样一个最小后端就足够了。

4.3 协同功能的业务化改造点

在实际业务里,我更关心的是这些协同操作能否和现有账号体系打通。你可以通过 WebSocket 服务端自行校验 token,把用户身份和光标颜色绑定,从而做出“谁正在编辑哪个单元格”的提示。Univer 支持设置用户名和光标信息,界面上会显示不同颜色的选中区域和编辑者名称,这对提升协作透明感帮助很大。

另一个业务化改造点是“只读与可编辑”的混合:比如一份报表,财务经理可以改公式,其他部门只能看,不能动。Univer 允许你动态切换实例或工作表的编辑状态。我的经验是在初始化配置里暴露一个allowEditing开关,根据当前用户角色从后端拿一个权限位来赋值,简单直接,不用去改内部源码。

5. 实战中的十来个雷:性能、布局与版本陷阱

5.1 版本与文档不一致,第一个大坑

我在接到一个内部项目时,第一步就是安装最新版@univerjs/preset-sheets。然后用旧文档里的new UniverPluginSetup写法,结果直接编译报错。后来翻 GitHub Issue 才发现,Univer 早期版本和后续版本在 API 命名和导入路径上做了多次拆包,比如把@univerjs/core的插件机制彻底重构了。

所以无论你看我这篇文章还是其他博客,都要记住一个原则:打开官方文档,看版本号是否与你锁定的版本一致。如果是 npm 安装,建议用精确版本号先固定:

npm install @univerjs/preset-sheets@0.2.11 --save-exact

不要在当前阶段频繁升级大版本,因为公开 API 还不像第三方成熟库那样稳定。锁版本能避免团队里其他人隔了一天 install 后拿到不同行为。

5.2 容器高度丢失导致白屏

Univer 的容器必须有明确高度。很多 React 开发者的第一版代码喜欢在父容器里写height: 100%,但父元素忘了设置高度,导致 Univer 初始化时拿到 0 高度,渲染结果就成了空白或一条细线。

我的处理方式是给承载容器一个固定高度,或者使用calc(100vh - 工具栏高度),并且在组件挂载后再初始化:

useEffect(() => { if (!containerRef.current) return const univer = UniverSheet.create({ container: containerRef.current, }) return () => univer.destroy() }, [])

记住:不要在组件还没渲染完成时就去new UniverSheet,那样拿不到真实节点尺寸。

5.3 大数据量下的性能观察

Univer 的 Canvas 渲染比 DOM 方案强不少,但也不是万能。我在测试一张 5000 行、10 列的数据表时,整体滚动和编辑还算顺畅,但如果每个单元格都计算了复杂公式,例如VLOOKUP或SUMIFS跨表引用,在公式数量达到 2 万以上时,初始加载明显变慢。

这个场景我建议在架构上规避:

  • 不让前端公式处理规模过大的关联计算;
  • 用setCellValue批量写入数据时避免触发逐次渲染;
  • 把低频更新的统计值放到后端算好,直接填充结果单元格。

Univer 的虚拟滚动策略能让“可视区域”的渲染成本趋于稳定,但公式计算引擎的计算量还是实打实的。如果确实需要在客户端算几万个复杂公式,建议分片或者放到 Web Worker 里,避免主线程卡死。

5.4 全局样式冲突:UI 被“污染”

在一次接入到 Ant Design 后台项目时,我发现 Univer 的工具栏偶发按钮变小、字体被全局 CSS 影响。原因是项目里有一条全局样式:

button { padding: 0 !important; }

Univer 的按钮被这条样式波及,观感立刻崩坏。解决办法并不是去复制一长串覆盖样式,而是给 Univer 的容器加一个独立样式作用域:

.univer-host { all: initial; } .univer-host * { box-sizing: border-box; }

或者利用 Shadow DOM 将其隔离。但在实际项目中改变宿主容器作用域可能会影响主题定制,所以我更推荐的做法是:在引入全局样式时对 UI 组件库的样式施加 scoped 限制,不要无差别覆盖原生元素。

5.5 打包体积和按需加载

如果你只用到电子表格,就没必要把文档和演示文稿预设一起打包。当前预设包设计上已经做了一部分按需,但还要注意官方文档提到的“locale 和第三方依赖”可能带来额外体积。一个粗暴但有效的优化手段是使用动态 import,让 Univer 只在你进入“表格编辑页”时才加载:

const UniverSheet = await import('@univerjs/preset-sheets').then(m => m.UniverSheet)

这样首屏体积可以省一大块。尤其在低配置电脑和移动端浏览器上,效果还是很明显的。

6. 在业务中落地 Univer 的路径参考

6.1 判断你的业务适合哪种接入深度

接入 Univer 不是非黑即白。我建议根据需求分三个级别:

  • 轻量级:用户改数据,系统存数据。只初始化实例,设置可编辑,然后在合适的时机把getSheetData()的结果 POST 到后端。这类实现不需要深究协同和导入导出,最快半天就能集成。

  • 中量级:围绕表格做一个业务编辑器。集成 Excel 导入导出,注册自定义公式,配置数据校验,再根据当前用户的权限设置是否可编辑。适合做数据填报、评分录入、计划编排等场景。

  • 重量级:把 Univer 作为多人协同业务画布。自建 WebSocket 服务,实现用户在线状态、操作广播、冲突处理,甚至对每次操作记录审计日志。适合做企业内部文档协作、项目管理台账这样的核心系统。

6.2 数据存储上的一点建议

很多朋友习惯把 Excel 表格直接序列化成一个 JSON 大对象存储。这在原型期可以,但一旦数据量大了,每次保存全量数据会很痛苦。我的建议是:

  • 把“表格结构数据”和“业务数据”分离。结构数据包括单元格样式、合并、公式等,这部分可以作为模板单独存储;业务数据则尽量映射到自己的业务表里,每行一条记录。

  • 如果必须整表保存,至少要加上版本号和增量操作日志。这样多人编辑时,恢复现场会容易很多。

  • 协同模式下,服务端最好保留操作序列,而不是只保留最终快照,否则某些历史状态会丢失。

6.3 关于“Univer 在线”的再理解

搜索热词里“univer在线”让我想到一个问题:需求方可能只是想要一个不需要下载客户端、能在浏览器里直接用起来的多人在线表格,而不一定在意底层 SDK。对于这类需求,Univer 官方本身有一个在线 Demo 站点可以直接体验,但对开发者来说,那只是让你感受效果。

真正的“在线”一定是要你自己掌握那一层同步逻辑。好在 Univer 把最复杂的表格客户端都做完了,你要做的只是让它和你的后端联通。换句话说,Univer 把“从 0 到 1”变成了“从 1 到 10”,剩下的每一步都可由你掌控。

6.4 社区和生态现状

我现在会在 GitHub 上关注 dream-num/univer 仓库的活跃度,它的提交频率是挺高的。社区里也有不少中文工程化相关的讨论,遇到问题时比较好搜到答案。不过因为是年轻项目,生产级文档还不够全面,部分能力需要翻源码确认。如果你决定在核心系统里依赖它,建议把关键场景写成自动化测试,固定版本,并锁定官方更新频次,避免某次升级打破你的功能边界。

写在最后的一个小建议

其实我最后想说的是,不要在动手写代码之前去买一堆昂贵的“在线表格私有化方案”。如果你需要的核心能力就是“在网页里编辑一张结构完整的表格”,Univer 已经完全能满足。先花半小时初始化一个 Demo,把你们的真实数据塞进去试试公式和滚动手感,再决定架构深度。按照我的经验,大部分人对表格的潜在需求,比他自己想象的要低一个量级——真正高频用到的可能只是数据录入、筛选、简单统计和导出,而 Univer 在这些方面都已经足够顺手。剩下你需要投入精力的,是把它的数据模型和自己的业务模型缝合好,这才是整个项目能不能长期维护下去的关键。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询