最近在调研在线表格技术方案的时候,大概率绕不开 univer 这个开源项目。它不是一个简单的 Excel 替代品,而是一套能直接嵌进自己应用里的在线办公套件:表格、文档、幻灯片都能做,核心用 Canvas 来渲染,性能跟传统 DOM 表格完全不是一个路子。界面默认就是中文,Excel 文件可以导入导出,公式、条件格式、图表、数据透视表、多人协同这些能力也都覆盖到了,对做 SaaS 产品、企业内部中后台、数据工作台这一类场景特别合适。
这套东西我已经在几个实际项目里跑过,包括接到 React 和 Vue 应用里、定制自定义公式、对接协同后端。这篇内容不打算照着官方文档念一遍,而是把选型思路、架构逻辑、集成时最容易翻车的点,以及一些排查经验整理出来,给正在做技术评估或者准备入手的团队做个参考。没有太多套话,都是实测下来的东西。
1. univer 为什么值得关注:在线表格赛道的痛点
1.1 传统表格嵌入方案到底卡在哪
很多团队一开始做在线表格,第一反应是用现成的开源 Excel 组件,比如 SpreadJS、Luckysheet、handsontable 这一类。但它们各有各的局限:有的是纯前端渲染,处理大数据量会明显掉帧;有的依赖 DOM 表格结构,行列复杂、样式一多就卡;有的导出 xlsx 需要额外付费授权;还有的根本做不了协同,只能在单机场景里勉强用。
这里有一个容易被忽略的问题:现代在线表格已经不是“把格子画出来”这么简单了。用户会要求公式实时计算、区域联动、条件格式、数据验证、多人同时编辑,还要和 Excel 互相兼容。如果继续用 DOM 节点去渲染几万行单元格,浏览器光是处理事件绑定和布局重排就会吃不消。传统方案最核心的矛盾,就是渲染能力跟不上业务复杂度。
1.2 univer 的核心能力清单
我做选型调研的时候,把 univer 的能力拉了一张表,对照下来它的覆盖面在同类型开源项目里属于很全的:
| 能力维度 | univer 支持情况 | 我实际用下来的感受 |
|---|---|---|
| 导入导出 | xlsx / csv | xlsx 兼容度比预期好,复杂样式会丢失一部分,后面细说 |
| 公式函数 | 内置 400+ 函数,支持自定义函数 | 公式引擎独立成包,不依赖界面,直接裸跑都可以 |
| 条件格式 | 支持常见规则类型 | 单元格规则、数据条、色阶用起来比较顺手 |
| 数据验证 | 下拉、数值范围、自定义校验 | 配置起来比原生 Excel 直观,适合做录入表单 |
| 图表 | 柱状、折线、饼图等 | 基础图表够用,美化能力还在迭代 |
| 协同编辑 | 基于 OT 的实时协同 | 官方提供协议参考,服务端要自己搭 |
| 文档与幻灯片 | 也在支持 | 目前功能完整度不如表格,但框架统一 |
最吸引我的倒不是这些功能列表,而是它从设计层面就为二次开发做准备:插件机制、命令系统、独立公式引擎、可替换渲染引擎,每个部分都可以单独拆出来用。
1.3 哪些团队适合用 univer
如果你的场景是“业务系统里需要一个表格”,答案基本是确定的:
- 做 SaaS 产品,要给用户提供在线编辑 Excel 数据的能力;
- 做低代码平台,需要拖拽生成的报表组件;
- 做内部数据工作台,要展示几千到几万行数据,同时允许业务人员直接修改;
- 做教育系统,需要在线习题录入、成绩表格;
- 或者只是想把 Excel 上传到网页上做在线预览和轻量编辑。
反过来,如果你的核心需求是“完整复刻桌面 Excel 的一切细节”,比如复杂的 VBA 宏、老式 ActiveX 控件、复杂的图表联动,univer 目前还做不到。它不是 Excel 的克隆版,而是一个面向在线场景重新设计的表格引擎。
2. 核心架构与设计思路拆解
2.1 从使用到内核:Univer 的分层模型
我第一次把一个完整的 univer 项目跑起来时,最大的感受是“包特别多”。表格核心是一套包,UI 是另一套包,公式引擎单独一个包,渲染引擎也单独一个包。乍一看很复杂,用熟了才发现这是它最值钱的地方:每一层都能独立替换和扩展。
我理解下来,univer 的架构大致分成四层:
- 核心层(Core):负责工作簿、工作表、单元格这些数据模型,以及命令管理、插件注册、选区模型。
- 渲染层(Render Engine):用 Canvas 绘制界面,处理视图层的刷新、缩放、选区状态。
- 功能层(Feature Plugins):表格 UI、公式、条件格式、数据验证、筛选、排序、图表等,全部以插件形式挂载。
- 应用层(Your App):你自己的业务代码,通过 API 和事件来读写数据,或者定制交互。
这种分层带来一个直接的好处:我可以不加载 UI 插件,只把公式引擎引入 Node 服务端,用来做数据校验和数据清洗。我在一个项目里就是这样做的,把 univer 的公式引擎单独打包成一个计算服务,业务端只负责发来公式字符串和参数,服务端算完返回结果。这个能力如果是从零开始实现,成本高到难以想象。
2.2 Canvas 渲染解决的核心矛盾
先解释一个很多新手会问的问题:为什么不用 HTML 表格渲染,非要用 Canvas?
因为表格数据的量级很容易爆炸。100 行 × 20 列就是 2000 个格子,如果用 DOM 实现,每个格子就是一个节点,加上边框、单元格样式、事件监听,浏览器要维护的节点树非常庞大。一旦到几万行,DOM 方案哪怕不做任何操作,光滚动就会卡顿。
Canvas 本质上是一个画布,所有格子都是绘制出来的位图像素。无论你是 100 行还是 10 万行,画布上的节点数量基本不变,只跟当前可视区域的大小有关。滚动时只需要重新绘制当前画布,配合视口虚拟化,让“行数”不再直接绑定“节点数”,这是 univer 性能好的底层原因。
我实测过用 univer 加载一个 3 万行 × 30 列的表格,正常滚动、编辑、切换 sheet 都算流畅。这在 DOM 方案里很难做到。但也要提醒一句:Canvas 渲染也有自己的短板,比如在 Canvas 上做文本选择、跨单元格复制粘贴、屏幕阅读器辅助支持,复杂度都比 DOM 高,univer 能做到目前这个程度,说明渲染层投入的工作量非常大。
2.3 插件机制:为什么“不加功能”也是一种功能
在线办公套件最怕的就是功能堆叠。如果每个团队都去改核心源码,项目很快变成一个谁都不敢碰的巨石。
univer 用插件机制把这个问题拆掉了。核心层只保留最基本的数据模型和命令总线,其余能力全部由插件注册。我实际用到的自定义场景包括:
- 注册一个自定义工具栏按钮,点击后调用后端 API 保存当前表格;
- 注册一个自定义函数,实现业务独有的库存计算逻辑;
- 在产品里弹出一个自定义侧边栏,读取选中的单元格数据;
- 拦截导出事件,在导出 xlsx 前自动替换掉敏感列。
每类需求都不需要改 univer 核心,只需要写一个符合插件接口的 class,注册进 Univer 实例就行。这比维护一个 fork 分支要舒服太多了。
2.4 命令系统与撤销重做
另一个值得展开的底层设计是命令系统。我在接入协同之前一直觉得命令系统是给自己找麻烦,直到真正要做多人编辑时才发现,如果没有统一的命令层,协同根本无从下手。
univer 里,用户做的每一个操作最终都会变成一条命令:插入行列是一个命令,设置单元格样式是一个命令,输入一个值是一个命令。命令带着参数走一个统一通道,既可以被记录到撤销栈中,也可以被发送到协同服务端。
这样设计有三个好处:
- 撤销/重做不需要针对每种操作分别实现,只要命令实现了 undo / redo 逻辑;
- 协同冲突处理只需要关心“命令之间的冲突”,不用管具体是改字体还是改数据;
- 通过 audit log 可以完整记录谁在什么时候做了哪个操作,方便排查问题。
这个思路跟游戏开发里经常用的 Command Pattern 是一样的,只是 univer 把它做成了完整的产品级实现。
3. 常用功能与实操要点:哪些做得顺手,哪些有坑
3.1 单元格与行列操作
先说最基础的编辑能力。univer 的表格体验已经做到和 Excel 很接近:双击进入编辑、回车向下移动、Tab 向右移动、公式自动补全、拖拽填充、Ctrl+C / Ctrl+V 复制粘贴,这些高频操作都覆盖到了。
我在实际项目里比较依赖的几个点:
- 冻结首行:在长表格滚动时非常实用,配置在 UI 上可以一键操作;
- 单元格合并:做报表头的时候几乎必用,跨行跨列合并都稳定;
- 隐藏行列:配合数据权限,可以把不需要展示的字段直接藏掉,导出的 xlsx 也保留这个状态;
- 自动筛选:表头下拉筛选加上排序,基本能满足普通用户在系统里做临时数据分析的需求。
唯一让我觉得还不完善的地方,是单元格弹出编辑框的体验,对于习惯桌面 Excel 的深度用户来说,还是有一点差距。好在这个场景可以通过自定义插件去优化,不至于被卡死。
3.2 公式引擎:不只是“算得快”
公式是 univer 的重点。它的公式引擎除了在 UI 界面里跑,还支持在纯 Node 环境下运行,这一点对复杂业务来说价值巨大。
我在项目里做了两个比较典型的事情:
第一是注册自定义函数。比如业务里有个“安全库存天数”的计算逻辑,涉及多个列,原来是在后端算完再返回给前端,现在直接在前端表格里写一个=SAFETY_STOCK(A2, B2, C2),公式引擎会自动调用我注册的函数,数据变化后结果会自动更新。
第二是把公式引擎放到服务端做批量计算。有一批历史数据需要按照新的计算规则重新跑一遍,如果是在浏览器里跑,数据一多就会把主线程卡住。我把规则改写成 univer 公式,在 Node 环境里创建了一个隐藏工作簿,把数据填进去,批量求值,速度比逐行写脚本要快,而且规则本身还能交给非开发同事在页面上维护。
这里有一个要注意的点:公式计算依赖关系是引擎自动收集的。如果你通过代码直接修改了单元格的值,正常情况下所有引用到该单元格的公式都会自动重算。如果遇到改了值公式不刷新的情况,优先检查是不是把单元格路由(CellRoute)写错了,或者数据更新时触发的命令没有走正式 API。
3.3 条件格式、数据验证和区块功能
这些功能看起来是“锦上添花”,实际上在业务系统里决定了用户是否愿意把一个在线表格当作工作入口。
我在一个排期系统里用过条件格式:如果截止日期离今天小于三天,整行背景变成浅红色;如果任务状态是已完成,整行变成灰色。配置逻辑跟 Excel 是一致的,规则写在 UI 上,保存后所有计算在本地完成,不需要后端参与。
数据验证我主要用在录入场景:比如“库存数量”这一列只能填大于等于 0 的数字,“优先级”列只能从下拉里选。这样从源头上减少脏数据,比后端再来校验要高效得多。
筛选和排序也值得一提。它们不是简单把当前页数据做排序,而是和协同、跨 sheet 引用这些能力在一起工作的。至少在我测试的场景里,筛选、排序、条件格式同时开启时,滚动和刷新没有明显卡顿。
3.4 图表与打印
图表方面,univer 支持柱状图、折线图、饼图这些基础类型,数据来源绑定表格区域,区域数据变化时图表自动更新。对于报表展示场景,这个能力足够。我在一个月报系统里把数据透视表和柱状图组合使用,基本替代了原来后端导出图片的过程,用户可以自己在页面上调整展示维度。
打印没有在 UI 层做太深的入口,导出 PDF 或者打印成纸张时,需要自行处理分页和边距。这个细节容易被低估,真正上线时才发现用户很看重打印效果。我目前的方案是前端用 univer 导出的 xlsx,再通过第三方库转 PDF,效果稳定,但多了一步。如果你主要负责内部系统,可以降低打印优先级;如果是直面用户的产品,建议提前规划。
3.5 协同编辑:要在服务端重点投入
多人实时编辑是 univer 的大卖点,但也是“看起来简单、落地起来复杂”的部分。univer 本身实现了 OT 算法的客户端部分,能够把用户的每个命令合并、转换、同步。但服务端需要自己实现,官方给出了协议规范和参考实现。
我建议的落地方式是:不要试图自己设计一套冲突合并逻辑,直接基于 univer 的协议文档做;刚开始只做“全量快照 + 增量命令”模式,也就是周期做全量存盘,期间的修改走命令日志。这样即使服务端崩溃,也能从快照加日志恢复到最近状态。
协同场景还需要额外考虑:光标位置同步、书签同步、需要房间和权限控制。这些 univer 都提供了接口,但业务层必须自己实现。
4. 从零集成 univer 到你的 Web 项目(实操记录)
4.1 环境准备
我先说结论:univer 对前端构建工具没有强绑定,Webpack、Vite、Rollup 都能用。我自己最常用的组合是 Vite + React 和 Vite + Vue 3,都没有遇到兼容性问题。
需要准备的依赖包括 Node.js 环境。安装包通过 npm 或 pnpm 管理,由于 univer 的包是 Split 成多个独立包的,建议直接用官方脚手架或者 preset 预设安装,避免自己逐个对齐版本。
我列一下大致步骤,实际包名以官方文档为准,因为版本更新比较快,刻意背下来反而不如会查文档重要。
4.2 最小可运行示例
一个最小可运行的 univer 项目,大致只需要几步:
npm install @univerjs/preset-sheets然后建立入口文件:
import "@univerjs/preset-sheets/lib/index.css"; import { Univer, UniverSheetsPreset } from "@univerjs/preset-sheets"; const univer = new Univer({ locale: "zhCN", }); univer.addPlugin(UniverSheetsPreset());在 HTML 里准备一个挂载点:
<div id="app" style="width: 100%; height: 600px;"></div>运行起来就能看到完整的表格界面。到这里已经把数据录入、公式、样式、导入导出能力都带起来了。如果你不想用预设,而是按需组合,官方文档里有各个插件的安装方式。
4.3 接入动态数据和导出 xlsx
实际项目中很少是空表让用户手动输入,更多是从后端拉数填充。univer 提供了数据模型接口,我们可以通过 API 初始化数据。
最常见的写法大概是这样:
const workbook = univer.getUniverSheetInstance("sheetId"); const sheet = workbook?.getActiveSheet(); // 遍历设置数据,这里用 setRangeValues 的方式会更高效 // sheet.setRangeValues({ row, column, value });不同版本 API 命名会有差异,但核心思路是一致的:先拿到工作表实例,再通过区域范围设置值。批量设置比逐格 set 快一个量级,所以尽量把后端数据组装成二维数组,一次性写入。
导出 xlsx 在 UI 层已经有内置的下载按钮,也可以调用 API 触发:
// 触发导出 univer.getPlugin("sheet").getExport();如果你需要在导出时做数据清洗,比如去掉隐藏列、把某些字段置空,建议在导出前先操作数据模型,导出完成后再恢复,而不是直接改后端数据。
4.4 性能调优与参数选择
跑通不难,但要在真实数据量下保持流畅,有几个参数和习惯我建议提前注意:
- 优先关闭不需要的插件。每个插件都会注册命令和监听事件,不用的插件会白白增加启动时间和内存占用。
- 大数据量场景下开启虚拟滚动。可视区域外的单元格不会被渲染,滚动条滑动时才动态绘制。
- 对单元格样式保持克制。每行每列都设置自定义边框和背景,Canvas 重绘压力会显著上升。能统一用表格整体样式,就不要逐格写样式。
- 频繁更新数据时,合并多个 setRangeValues 调用。短时间内多次更新某个区域,会触发多次重绘,合并成一次可以明显减少白屏和闪烁。
我遇到过最典型的性能问题是:初始化时往一个 5 万行表格逐行插入图片。虽然 univer 进行了异步渲染,但网络请求和图片解码仍会拖慢首屏。最后改为分页加载,默认只渲染前 200 行,滚动到底部再追加数据,体感就正常了。
5. 常见问题与排查技巧实录
5.1 打开大文件或大数据量时卡顿
如果你的表格一上来就有几十万行,任何前端方案都没有捷径。先明确一个原则:在线表格不是给用户铺开几十万行直接编辑用的,而是“把数据开放给用户看和操作”。
我建议的缓解手段有三级:
- 第一级:后端分页或按需加载,前端只展示当前业务需要的数据范围;
- 第二级:use univer 的虚拟滚动,开启后大表格滚动性能会明显改善;
- 第三级:关闭非必要的图表和条件格式插件,这些功能在大数据量下加了额外的渲染和计算开销。
如果还卡,就要考虑从产品层面拆分视图了。我做过一个项目,原本一个 sheet 塞 30 万行,后来拆成 10 个 sheet,每个 sheet 3 万行,性能和体验都上来了。
5.2 xlsx 导入导出的兼容性差异
xlsx 是个复杂的格式,任何开源方案都不可能 100% 还原 Excel 的所有细节。univer 目前对常见功能支持很好,但仍有几个我踩过的差异点:
- 复杂图表:Excel 里做过深度定制的图表,导入后可能退化成简单的形状或丢失部分配置;
- 透视表:部分透视表缓存和布局信息无法完整还原;
- 宏与控件:VBA、ActiveX 这些完全不在支持范围内;
- 条件格式的高级表达式:部分复杂规则会被忽略。
遇到差异,我的经验是不要试图在表格组件里兜底,而是提前在导入流程里做检测,发现不支持的要素时给用户弹一个提示,说明哪些信息会丢失,让用户确认后再继续。这个提示虽然是一次性的,但能少很多客诉。
5.3 公式不更新或结果异常
公式重算机制总体可靠,但有几个高频原因会导致结果不对:
- 手动修改了路由或索引,公式引用还是旧位置;
- 自定义函数没有正确注册,或者注册时机晚于公式求值;
- 循环引用未开启,Excel 里能兜底,univer 出于性能会直接阻断;
- 某些区域被设置为纯文本格式,输入公式不会触发计算。
排查方式也有套路:先在浏览器控制台拿到当前公式引擎的状态,再挨个核实依赖关系。我做过的快速判断方法是新建一个简单 sheet,只输入=SUM(A1:A10),如果也不对,那问题大概率在引擎本身或插件版本;如果简单公式正常,再去检查业务自定义函数。
5.4 协同编辑的冲突与卡顿
协同场景里最常见的坑不是“多人同时改同一个单元格”,而是“服务端拿到命令后无法正确合并”。
最安全的行为是保证服务端严格按照命令时间排序,对同一位置的并发修改做 OT 转换。早期我图省事,直接让后提交的覆盖先提交的,用户反馈说“我明明刚改完,他那边一保存就没了”。后来换成了基于 OT 的合并逻辑,体验才正常。
还有一点要注意:协同状态下的撤销操作要特别注意。A 用户的撤销不能撤销 B 用户的操作,这跟单机版的撤销机制完全不同。务必测试多端同时打开、交叉操作的场景,而不是只在单浏览器里测。
5.5 依赖升级和多包版本对齐
univer 拆包多,发布节奏也不完全一致。如果升级时只把一个核心包升了,其他插件还是老版本,很容易出现运行时异常,最常见的是 “Cannot read properties of undefined”。
我现在都是统一使用预设包或者严格对齐版本号的组合。只在发版前统一执行一次升级,而不是随手npm install某个包。团队如果规模比较大,最好在 CI 里检查所有@univerjs/*包版本一致性,避免线上环境炸掉才排查。
6. 一些个人体会:别把 univer 当成 Excel 替代品
用了这一段下来,我最深的体会是:univer 的价值不是“复刻 Excel”,而是“提供一个可以编程的基础设施”。真正复杂的功能,比如业务公式、数据权限、协同服务端、导出合规,都需要开发者在它之上做很多工作。它能做的,是把最耗时、最容易出错的表格绘制、公式计算、格式兼容这些底层问题扛掉,让团队把精力放在自己的业务逻辑上。
如果你正准备引入它,我建议先做一个最小验证:把一个真实的业务表格导入,跑一下你的核心操作(编辑、公式、导出),量一下性能。不要一开始就追求全部功能,先把主链路跑通,再慢慢叠加协同、图表这些高级能力。
最后分享一个小技巧:在做技术选型评审时,不需要把 univer 跟 Excel 或者 commercial 产品做逐像素对比。你只需要写一个测试用例,覆盖你们业务里最典型的 20 个操作,然后分别用候选方案跑一遍,看谁在真实数据量下最稳。univer 目前在我用过的诸多方案里,是综合得分最高的那个,而且它还在持续迭代,值得保持关注。