1. 先搞清楚的事:Univer到底比“自研表格”强在哪
1.1 看到Univer这个名字,先别急着和一个“表格组件”划等号
Univer最近在表格/在线文档产品圈里出现频率很高。如果你以前玩过Luckysheet、Handsontable、SheetJS这些开源方案,大概率会拿它做对比。我的第一反应也是:这不就是一个新的Web电子表格组件吗?还能玩出什么花?
实际深入了解之后,我得纠正这个印象。Univer不是单纯一个可嵌入的表格控件,而是一套定位在企业级在线文档场景的“文档基础设施”。官方把它的能力拆成三块:Univer Sheets(电子表格)、Univer Docs(文档)、Univer Slides(演示文稿),而这三类产品共享同一个底层数据模型和渲染引擎。再直白一点:你用同一套代码逻辑,可以同时搭出在线表格、在线文档和在线幻灯片,并且这三者之间可以互相引用、互相嵌套。这个设计思路,才是Univer真正值钱的地方。
如果你正在做低代码平台、教育SaaS、数据分析后台,或者任何需要在Web里展示和编辑结构化数据的系统,Univer都值得认真评估一遍。我个人评估下来的结论是:它解决的不只是“在网页里画一个表”,而是“怎么让你的业务数据在浏览器里被专业地编辑、计算、协同和导出”。
1.2 三条路线摆在一起,为什么最终会走到Univer这里
很多团队做Web表格功能,刚开始都是三条路线选一条。
第一条是自研。听上去自由度高,但表格是一个典型的“看起来简单、做起来深不见底”的领域。单元格编辑、选区、复制粘贴、公式引擎、行列冻结、筛选、合并单元格、样式系统、撤销重做、性能优化……任何一个模块都足够一个小组忙上两个月。等到你的业务开始需要“在线协同”的时候,自研成本会直接变成指数级上升。
第二条是直接买商业产品。像SpreadJS、Handsontable的非开源版本、或者集成腾讯文档/飞书文档的能力,这些方案成熟稳定,但有两个麻烦:一是费用不低,二是定制自由度有限。你很难把表格的交互细节改造成符合自己产品气质的样子,数据也不一定愿意托管到别人的服务上。
第三条是开源组件二次开发。Luckysheet和Handsontable大家用得最多。这条路快,但天花板肉眼可见:Luckysheet的核心代码停留在上个时代的架构上,Canvas渲染和复杂交互耦合在一起,想要加入自定义类型、多端协同、服务端渲染这些能力,改动起来非常吃力。手捋一遍源码之后,你会发现大部分时间都在跟原来的设计约束做斗争,而不是在实现自己的业务逻辑。
Univer选择的是第四条路:开源核心,架构先行。它把文档数据模型从具体产品形态里剥离出来,做成了一套通用抽象,Sheet只是这套抽象的一种呈现。你基于同一个模型去写表格、文档、幻灯片,甚至自定义一种全新的文档类型,路径是一致的。这对我来说是决定性的理由:我不需要在未来某一天推倒重来。
1.3 适合谁用,不适合谁用,先把预期管理好
Univer适合的场景,我总结成四类:
- 需要深度定制表格能力的SaaS产品,不只是“一个表格展示页面”。
- 低代码平台里内置数据编辑控件,要求交互专业、可扩展。
- 教育、数据分析、企业资源规划类系统,需要公式计算、数据透视、条件格式。
- 对协同有明确规划的团队,希望从第一天架构就为多端协同留好位置。
不适合的场景也有:如果你的需求只停留在“把数据用表格形式展示出来,最好能一键导出Excel”,那Univer确实杀鸡用牛刀了,用轻量组件更省事。或者团队根本没有前端工程化基础,只想引入一个JS文件就完事,那Univer的多包架构一开始会给你带来一点复杂度压力。但话说回来,这种复杂度压正在保护你未来两三年不被需求追着跑。
2. Univer的核心架构:一套模型把Sheet、Doc、Slide全部统一
2.1 UOM和uni-impl:文档本质上是一棵对象树
Univer架构里最底层的概念是UOM(Univer Object Model),中文可以理解成“Univer对象模型”。这套模型把所有文档内容抽象成对象树和节点,Sheet表格、Doc文档、Slide幻灯片都构建在这棵对象树之上。
打个比方:传统表格组件把数据存在二维数组里,每个单元格是数组里的一个元素。这种结构简单,但一旦要表达“这个单元格里嵌了一段富文本”“这个单元格的值来源于另一张表的引用”“这个区域被合并了”这类复杂关系,二维数组就力不从心。UOM则把单元格、行列、文档段落、幻灯片图层全部建模为对象节点,节点之间可以互相引用、嵌套和关联。
Univer还内置了一门叫uni-impl的描述语言。你不一定需要深入学它,但要知道它的存在:它允许你在UOM对象树里描述样式、公式、数据校验、自定义节点类型等语义。换句话说,表格里不只能存值,还能存“这个值的计算逻辑是什么”“这个区域的数据从哪来”。
这个抽象带来一个直接好处:不同文档类型不是“不同的组件”,而是“同一套数据模型的不同视图”。Univer官方主推的“文档内嵌表格、表格内嵌文档、幻灯片图表直接引用表格数据”,都建立在UOM之上。如果你用过Notion,可以类比理解为“Block是一切的原子单位”,Univer的原子单位就是UOM里的Node。
2.2 依赖注入和插件注册:为什么“加一个功能”是加法而非改造
我刚开始看Univer源码时,印象最深的是它把整套功能都做成了可插拔模块。Univer的每个能力域,比如Sheets、Docs、UI渲染、公式计算,都是以插件形式注册到核心容器里的。这个设计在工程上和Android的组件化、Spring的依赖注入类似:核心只负责管理生命周期和模块通信,具体能力由插件按需提供。
作为集成方,这个设计的好处很实际:
- 你不需要的功能可以不注册插件,包体体积会明显下降。
- 你可以在不修改源码的前提下替换某个默认实现。比如默认的导入导出、默认的右键菜单,都可以注入自定义实现。
- 代码不会变成“无人敢动的一团”。
我在实际项目里的感受是,插件边界清晰意味着团队可以并行开发,前端小组负责界面交互,一些人专门做自定义节点类型,数据小组只关注模型层。如果架构没有先想清楚,人一多就会互相踩脚。
2.3 Formula引擎:Univer的公式不是“抄一个eval”
表格和普通网格的本质区别在于公式引擎。Univer的公式引擎是TypeScript实现的一套完整计算框架,支持函数、引用链、区域运算、动态数组、循环依赖检测。它不只是一个“把字符串变成计算结果”的解释器,更像一个带依赖追踪的计算图。
我举个例子,Excel里的公式重算策略是:修改A1,系统自动找所有依赖A1的单元格并更新。Univer做的是同一件事:它维护公式之间的引用链关系,任何一个单元格值变化,触发的是整条依赖链上的定向重算,不是全表暴力计算。这就是为什么在大表格场景下,Univer相比老式表格组件有明显性能优势。
更关键的是,公式引擎也是插件化的。你在文档里引用表格区域、在幻灯片里展示表格聚合结果,底层都是同一个公式引擎在协同工作。这种“全产品域打通”的能力,是其他开源表格组件暂时给不了的。
3. 从零跑通一张在线表格:完整集成记录
3.1 确认包结构和依赖关系,别一上来就复制官方Demo
Univer的包是多包结构,以我使用1.x主线版本的体验来说,最基本的集成会用到这几类包:
| 包名 | 作用 |
|---|---|
@univerjs/core | 核心模型和生命周期,必须最先安装 |
@univerjs/sheets | Sheet产品域的模型与命令逻辑 |
@univerjs/ui | 渲染和交互基础设施,必须依赖 |
@univerjs/sheets-ui | Sheet的UI渲染层,就是你能看到和操作表格的那部分 |
@univerjs/preset-sheets | 官方提供的快速启动预设,帮你一次引入常用插件 |
这里有个新手容易踩的小坑:只装了core和sheets,结果表格渲染不出来,因为没有UI插件。UI、产品域、产品域UI是三个层级,漏了最后一层,数据模型在后台默默运作,但屏幕上什么都没有。
我在做技术选型评估时,第一件事不是看Demo,而是看包体积和依赖关系。Univer的包管理在大型项目里是加分项,但前提是你得理解它的分层逻辑,否则会觉得一个表格怎么要装这么多包。
3.2 最小代码:初始化Univer实例
以TypeScript为例,最简可用版大概是这样的(版本迭代较快,具体API以官方文档为准,但思路稳定):
import { Univer, LocaleType, UniverInstanceType } from '@univerjs/core'; import { defaultTheme } from '@univerjs/design'; import { UniverSheetsPlugin } from '@univerjs/sheets'; import { UniverSheetsUIPlugin } from '@univerjs/sheets-ui'; import { UniverUIPlugin } from '@univerjs/ui'; const univer = new Univer({ locale: LocaleType.ZH_CN, editions: [ { name: 'my-first-workbook', type: UniverInstanceType.SHEET, }, ], plugins: [ UniverSheetsPlugin(), UniverSheetsUIPlugin(), UniverUIPlugin({ container: 'univer-container', header: true, toolbar: true, footer: true, themes: { defaultTheme }, }), ], });editions这个词如果你看官方文档,可能会被翻译成“编辑区”或者“文档实例”。我的理解是:它定义了你一开始要创建几个什么类型的文档对象,这里就是“新建一个Sheet类型的工作簿”。这是从Univer 1.x开始的设计,老版本的创建方式不一样,所以你搜网上的教程时一定要先确认版本阶段。
初始化之后,浏览器里指定id的容器就会渲染出一个完整的工作簿界面:工具栏、行号列号、公式栏、标签栏都齐了。到了这一步,Univer的体验和其他表格组件其实差不多,接下来才是区别所在。
3.3 把真实数据喂进去,同时学会监听用户操作
零散的表格壳子没有任何意义,你得把业务数据塞进去。Univer提供了两种操作路径:
第一种是直接通过实例API拿当前表格,然后操作Range对象:
const sheet = univer.getActiveSheet(); const range = sheet.getRange(0, 0, 3, 3); // 前三行前三列 range.setValue([ [1, 2, 3], [4, 5, 6], [7, 8, 9], ]);第二种是监听用户交互。我的经验是,在线表格产品里,“数据展示”只占一半工作量,另外一半是“用户改了表格之后你的业务系统怎么感知”。
sheet.onCellClicked((cellInfo) => { console.log('用户点击了单元格', cellInfo.row, cellInfo.col); }); sheet.onSelectionChange((selection) => { console.log('用户选择区域变化', selection); });事件系统对做复杂业务非常关键。比如你在教育产品里做成绩表格,班主任修改了一个学生的分数,你的后端需要接到这个变化并触发新的排名计算。没有事件监听,你只能用“保存按钮触发全量提交”这种笨办法,交互体验会落后一个时代。
3.4 公式和导入导出:这两块开箱能力决定成败
表格产品绕不过公式和Excel导入导出。Univer的公式能力开箱即用,默认注册了一大批常用函数,这意味着用户在网页里输入=SUM(B1:B10),结果会实时计算,不需要你为每个公式单独开发解析器。
导入导出方面,Univer通过@univerjs/sheets-import-export插件体系支持加载/保存多种文件格式,其中xlsx是最常被问到的格式。集成的时候,需要按官方文档配置对应解析器和序列化器。这块我建议在项目早期就验证一次,因为“能不能把客户辛辛苦苦做的Excel表格无损导入”,往往是判断一个表格组件合不合格的最粗线标准。
我之前见过某个项目用轻量组件搭的表格,Excel导入后样式、合并单元格、批注丢了一半,客户直接拒收。Univer在这块的完成度是明显偏高的,毕竟它定位就是企业级文档基础设施,导入导出属于基本功,不用指望所有格式100%还原,但主流场景覆盖是够的。
4. 实际联调中的四个坑,每个都让我多花了一个晚上
4.1 第一个坑:同一页面多个Univer实例之间的配置隔离
我的第一个实战需求是在一个页面里同时渲染两个互不关联的表格。按照惯性思维,我创建了两个Univer实例,并指定了两个不同的容器,结果第二个实例初始化时,把第一个实例的UI配置覆盖了,工具栏、主题都乱了。
排查后发现,Univer的依赖注入容器虽然提供了隔离能力,但默认配置下,多个实例共享了一部分全局级别的状态。正确的做法是在创建每个实例时,给不同的实例指定独立的依赖注入容器标识,并且显式区分插件实例的命名空间。
这里给正在评估Univer的同学提个醒:如果你的产品是“一页面多表格”的形态,一定要在技术设计阶段就把多实例问题摆到台面上。Univer不是不能多实例,而是要你理解它的容器模型,额外做一层配置。官方文档和多实例示例是绕不开的功课。
4.2 第二个坑:Fast Refresh状态下旧实例挂载残留,页面越调试越卡
开发环境开启Vite的热更新时,会频繁改代码触发页面自动刷新。Univer实例如果在开发调试中反复初始化,浏览器里会残留大量未被正确销毁的Canvas渲染层,内存占用肉眼可见地往上涨,页面也开始卡顿。
这个坑不只在Univer身上,任何用了复杂Canvas渲染、手动管理DOM生命周期的库都会遇到。但Univer的设计让它更容易表现:它创建的容器层级比较多,如果快速刷新只更新了UI插件而没有强制整页刷新,旧实例并不会自动清理干净。
避坑方法是设置一个全局的Univer实例引用,在每次重新初始化之前,显式调用销毁方法清理旧的实例和事件监听。简单说:不要让代码里存在“同一个容器被new了两次”的情况。这是我在开发环境被卡了半天才总结出的教训。
4.3 第三个坑:主题和样式优先级,和UI框架打架
Univer自带一套基于Design Token的主题体系,这在表格组件里是加分项。但它和项目里其他UI框架的样式冲突也是真实的:比如Univer的弹窗、Tooltip、下拉菜单,和公司内部组件库的样式互相覆盖。一旦你同时引用了Ant Design、Element Plus这类重量级库,全局样式冲突就暴露出来了。
我的处理经验是给Univer的容器节点单独划一个样式作用域,用CSS层的隔离思想防止样式互相传染。不要指望Univer的类名前缀能解决一切,因为UI框架的全局Reset样式你控制不了。官方提供的主题Token这时候就派上用场,你可以把Univer主色、字号、间距统一定义成和产品设计系统一致的变量,减少“必须要用样式hack”的冲动。
4.4 第四个坑:服务端渲染场景下的规避方案
如果你的产品是用Next.js/Nuxt这类框架做服务端渲染,Univer这种依赖浏览器Canvas和DOM API的组件,在服务端是不能直接初始化的。这个问题很典型,不算Univer的缺陷,但它确实需要你在架构上多一点思考。
我的做法是动态加载组件,只在浏览器端导入Univer相关代码。配合一个“组件挂载完成后再初始化”的生命周期钩子,确保SSR过程中不会执行到任何Univer代码。Univer的包是按模块拆的,天然适合动态import,这一点比老式的整体引入组件要友好得多。如果你的系统里CSR和SSR混合使用,尽量把所有Univer交互放在独立的客户端路由切片里。
5. 再往下走:公式、协同与二次开发的边界
5.1 基于UOM做二次开发,自定义单元格类型并不难
表格业务永远少不了自定义需求。我们当时琢磨的一个需求是:在单元格里直接渲染一个小型进度条,进度数据来自接口,用户点击进度条可以跳转详情。
放在老表格组件里,这类需求要么用单元格富文本拼凑,要么只能在右键菜单里放一个Web组件入口,体验很割裂。Univer的UOM模型允许定义自定义单元格内容类型,你可以在对象树上挂自定义节点,渲染层识别到这个节点类型后再绘制对应UI。实现路径是:注册自定义节点类型 -> 在模型层定义节点数据 -> 在渲染层处理绘制和交互。
这个能力让Univer不是“UI表格壳子”,而是一个真正的文档应用框架。当然代价是学习链路变长,你需要理解UOM的节点定义方式、渲染层的对象识别逻辑、以及命令系统如何交互。但比起老方案里“绕过组件自作主张画画”的野路子,Univer给了你一条正统路径。
5.2 协同能力:开源边界与工程化距离
Univer最有吸引力的一点,是它从架构底层就考虑了在线协同。传统的表格组件,协同属于“后加功能”,需要你把表格状态改成可同步的状态机,再配合WebSocket、操作转换(OT),工程量巨大。
Univer采用的思路是记录重放。用户在表格上的每次操作,本质上都是一条可序列化的命令,服务端把这些命令按顺序重放,就能得到一致的最新状态。项目到这会涉及一个热门词:多人同时编辑、实时同步、协作冲突处理,这些都是表格类产品高端功能的核心。
需要明确的是,Univer在开源层面开放了协同相关的基础能力和底层SDK,让大家可以在此基础上搭建协同功能;而完整的协同套件,包括身份系统、权限、头像光标、评论、实时状态展示等工程化能力,属于官方提供的企业级服务范畴。对于想完全自建协同的团队,这意味着你需要在开源能力之上再开发一套适合自己业务的协同服务端。已经有网友分享过用Yjs这类CRDT库做数据同步适配的经验,这条路可行,但我建议你先跑通最小闭环再铺开,协同的复杂度很容易低估。
5.3 一个实际的选型判断框架
结合自己这次从0到1的评估和落地,我总结出几个判断标准,供正在纠结的团队参考:
- 如果未来两年内业务可能从“单表格”长成“文档+表格+数据看板”的形态,选Univer,因为它统一了模型。
- 如果你的核心诉求是HTML表格、基本编辑和导出,Univer有点过重,轻量组件更快。
- 如果你的团队有2个以上前端,且能做模块级维护,Univer的插件架构会越用越顺。如果只有1个前端且还要兼运维,建议先想清楚能否驾驭多包架构。
- 如果客户拿着Excel刁钻格式来挑战你,Univer的高还原度导入导出会是你的护身符。
- 如果协同是非做不可的,尽早围绕Univer的命令系统设计接口,别等服务端同事开始写代码了才返工。
我个人在实际使用中的体会是,Univer这种项目最不适合的做法就是“先跑起来再说”。它的能力边界和架构理念需要你在头脑里先形成一个完整认知模型,否则你只会把它当高阶Luckysheet用,碰到需求超出边界时就又回到“绕组件”的老路上。反过来,一旦你理解了UOM、Dependency Injection、插件系统这三件事,Univer能给你的自由度是非常高的。
最后再分享一个小经验:无论你是做的什么项目,表格都不是单纯的前端组件问题。把它想成“结构化数据在浏览器里的生产力工具”会更准确。Univer恰好把手伸到了这个领域的基础层——能不能用好它,不取决于你会不会写配置代码,而取决于你对自己业务数据结构理解有多深。