☰
Univer架构详解与Web表格集成实战:从公式引擎到协同编辑
2026/9/26 2:11:45 网站建设 项目流程

做数据产品这两年,我一直在跟“网页里怎么塞一个能用的表格”这件事较劲。自研过基于虚拟滚动的表格组件,写到公式联动那一步就发现复杂度失控;也试过直接在项目里嵌一堆现成表格库,结果商用的商用、卡顿的卡顿。后来接触到Univer这个开源项目,第一次跑通示例时说实话心情挺复杂的——很多我计划花几个月从零写的东西,它已经作为一个框架做好了。这篇文章不打算照着官方文档念,重点聊聊我对Univer架构的理解、实际接入过程中的体验和踩坑记录,给正在做Web表格选型的朋友们一个参考。

1. 从Luckysheet到Univer:这个项目到底在解决什么问题

1.1 一段绕不开的前史:Luckysheet确实火过

如果你2020年前后就在搞Web表格,大概率听过Luckysheet。它当时在国内开发者社区里的热度很高,基于浏览器实现Excel式交互那一套做得相当早,很多人拿来给自己的后台系统加在线编辑能力。我早期也调研过它,功能确实全:单元格样式、公式、筛选、冻结窗格都有。但真要在生产环境里深度定制时,问题就来了——Luckysheet的底层架构还是偏成熟的插件DOM渲染思路,对于复杂公式计算、大规模数据渲染、多人协同这些高难度动作,扩展起来很吃力。也就是说,它的定位更像一个“功能丰富的表格组件”,而不是一个能让你做二次平台开发的框架。

Univer正是这个背景下出现的。它和Luckysheet同出一门(梦数科技团队),但项目定位完全不一样。从命名和包结构就能看得出来——它不再只是一个sheet组件,而是同时覆盖Sheet、Doc、Slide三种文档形态的办公套件框架。我接触到的资料里,团队的目标是打造一套面向Web的开源办公基础设施,而不是又一个Excel克隆。

1.2 一句话理解Univer的定位

如果非得用一句话概括:Univer是一个基于TypeScript、可插拔、自带渲染引擎和公式引擎的Web办公套件框架。你可以把它理解成“供开发者自己组装和扩展的高性能Office组件底座”。它默认给你一个能用的多工作表编辑器,但真正的价值在于——你能像搭积木一样挂载公式、条件格式、数据透视表、协同编辑这些能力,甚至可以继续挂文档和幻灯片模块。

这个定位决定了它的受众。如果你只是想在后台管理页面里放一个可读写的表格,Univer当然能做,但杀鸡用牛刀了;如果你的产品要的是“用户能在网页里完成从录数、算数到导出报表的完整链路”,甚至还想在同一个框架里兼容文档编辑,那Univer的投入产出比就非常明显。另外它对拥有前端团队、但不想从零写渲染和协同协议的公司来说,是很值得研究的一条路。

2. 架构设计里最打动我的三个点

2.1 插件化机制:包体积是“按需购买”的

Univer的工程结构是最让我眼前一亮的地方。它没有把几十个功能全堆在一个包里,而是拆成了多个独立包,每个能力都是一个插件。比如@univerjs/core是核心数据模型,@univerjs/sheets是表格能力,@univerjs/sheets-ui是交互界面,@univerjs/engine-formula是公式引擎,@univerjs/sheets-formula负责把公式能力注入表格。你在工程里想要什么,就注册什么插件。

这样做最直观的好处是包体积可控。只做数据看板,只引入core和sheets;要做在线公式编辑器,再引入engine-formula和sheets-formula。用打包工具做tree shaking时,未注册的插件根本不会被打进产物。这和很多传统表格库“一次全给你,能不能删再说”的粗暴做法相比,对业务系统的性能优化友好太多。而且插件机制不只是挂在初始化上,它还贯穿了UI层——菜单项、右键菜单、快捷键、工具栏按钮都能通过插件扩展,我后来做业务能力扩展时几乎没有改动过Univer源码。

2.2 命令系统和好用的Undo/Redo框架

另一个让我觉得“这项目设计得很认真”的地方是命令模式。Univer把用户操作抽象成一个个Command,例如修改单元格值、删除行、调整列宽,本质上都是在派发对应命令。数据层接到命令后先执行,再通过统一机制收集变更结果。

这个设计带来两个立竿见影的好处。第一,Undo和Redo不需要你逐业务去手写快照,它天然基于命令流就能回溯,我在做编辑类功能时省了大量精力。第二,这也为多人协同打下基础——如果所有操作都是命令,那么服务端收到的就是有结构、可同步的“操作记录”,而不是一整个工作簿快照。Univer的协同模块就是建立在这套命令体系之上的,这点比那些“每次全量保存JSON”的表格库要先进一个量级。

2.3 渲染引擎:为什么它能扛住上万行不卡

Univer的渲染层是基于Canvas 2D实现的,这跟很多传统表格库用DOM去拼单元格的路线完全不同。用DOM渲染Excel式网格,最大的痛点就是DOM节点数量一上去就卡爆,浏览器对上千个独立DOM节点的布局计算会很吃力,更别说单元格边框、选中态、公式高亮这些交互效果。Canvas的绘图模式则绕开了这个瓶颈,它没有成千上万的DOM节点,渲染时直接在一张画布上根据视口区域重绘。

不过Canvas的代价是“没有天然的DOM节点可点”,所有交互命中都要自己做坐标计算。Univer为此抽象了一层渲染引擎,内置了视口裁剪、分层绘制、局部刷新这些逻辑。实际使用中,我在一个项目里放了一万五千行、二十列的表格数据,滚动和选中操作依然很顺滑。这种断层级的性能表现,是自研方案很难快速追上的。

2.4 独立公式引擎:计算链路没有和UI耦合

公式是电子表格的灵魂,也是很多Web表格方案最容易翻车的地方。很多开源表格库的公式模块是和UI逻辑强耦合的,想加一个新函数,得顺着它庞大的内部结构一路摸过去。Univer把公式引擎单独拆成了@univerjs/engine-formula,核心负责解析公式、管理函数库、维护计算依赖关系,和UI渲染完全解耦。公式结果最终会通过订阅机制触发单元格更新,不管这个公式是在A1单元格还是跨工作表引用,都能被引擎正确追踪。

这里有个细节值得展开:公式计算如果做成简单遍历,表格一大就会明显卡顿。Univer的公式引擎在设计上考虑了依赖图和计算链的维护,一个单元格的值变了,只有被它影响的公式单元格才会重新计算,而不是把整张表所有公式全跑一遍。理解了这个修正机制,你就能明白为什么复杂工作簿在Univer里依然能保持不错的响应速度。

3. 把Univer跑起来:我的集成实操记录

3.1 环境准备:Node版本和包管理器

我先说结论:建议直接用Node 18以上版本,包管理器用pnpm或者npm都行。Univer是多包Monorepo工程,对依赖提升和版本一致性比较敏感。如果用npm,装包时最好用--save-exact把Univer相关包的版本精确锁住,不要用^,否则某一天装到一个小版本漂移,可能就会出现莫名其妙的行为差异。我当时项目的Node版本是16,装完跑起来报了一堆OpenSSL相关的错,查了一圈才发现是Node版本太旧,升级到18后一切正常。

3.2 最小示例:五步渲染一个可编辑工作簿

我以当时用的1.x版本为例写一个极简示例。因为Univer的API在不同小版本里变动有些频繁,建议你接入时以官方最新示例仓库为准,我这里重点演示心智模型。

import { Univer } from '@univerjs/core'; import { UniverSheetsPlugin } from '@univerjs/sheets'; import { UniverSheetsUIPlugin } from '@univerjs/sheets-ui'; import { UniverFormulaEnginePlugin } from '@univerjs/engine-formula'; import { UniverSheetsFormulaPlugin } from '@univerjs/sheets-formula'; const univer = new Univer(); univer.registerPlugin(UniverSheetsPlugin); univer.registerPlugin(UniverSheetsUIPlugin, { container: 'app', }); univer.registerPlugin(UniverFormulaEnginePlugin); univer.registerPlugin(UniverSheetsFormulaPlugin);
<div id="app" style="height: 100vh;"></div>

这段代码做的事情:创建Univer实例,注册表格核心能力、表格UI能力、公式引擎和表格公式插件,然后挂载到页面上一个高度为100vh的容器里。做完这些,浏览器里就已经有一个支持多工作表切换、单元格编辑、常用公式计算的基础在线表格了。

如果你用的是1.x,还可以通过Facade层拿到更友好的API来操作工作簿:

import { FUniver } from '@univerjs/facade'; const univerAPI = FUniver.newAPI(univer); univerAPI.createUniverSheet({ name: '员工提成表' });

这套API有点像操作Excel的宏,能让你在业务代码里直接新建工作簿、读写单元格、监听选中变化,不必深入Univer内部事件流。对大部分业务集成来说,这层接口够用了。

3.3 踩坑实录:样式丢失、版本漂移和容器高度

第一个坑是样式。Univer的UI组件设计了一套设计语言包(@univerjs/design),但如果你只装了核心包而忘了引入样式文件,界面会呈现一种“光有逻辑没有衣服”的状态——工具栏按钮是错位的,弹窗样式完全失效。我当时按官方示例装了@univerjs/design,并且确认样式被正确引入后,界面才正常。这一点在集成时最容易漏,而且报错信息不一定明显,所以建议初始化后先截个图确认UI渲染正常再继续。

第二个坑是多包版本漂移。Univer包里彼此有peer依赖,如果core是1.0.x,sheets-ui却是1.1.x,运行时很可能出现“插件注册失败”或“命令未找到”之类的异常。解决方式也很简单:要么所有@univerjs开头包统一版本号,要么直接复制官方examples里的package.json。不要自作聪明地给某个包单独升版本。

第三个坑是容器高度。官方示例里容器节点通常直接给了固定高度,如果你在业务代码里忘了设高度,表格会渲染成一个高度为0的DOM区域,看起来就像“什么都没有”。排查这个问题时我一度以为是构建配置有问题,后来才发现是CSS的锅。所有嵌入Univer的容器节点,务必在样式里明确高度。

第四个坑和React 18的严格模式有关。如果在开发环境启用了StrictMode,组件可能会被渲染两次,这时如果你在组件副作用里创建Univer实例,就会在一个容器里初始化两个实例,控制台会刷出冲突警告。稳妥做法是把Univer实例放在模块级单例里,或者用一个ref判断是否已经初始化,再注入容器。

4. 把Univer嵌入真实业务:公式、导入导出和前后端打通

4.1 自定义公式:给表格装上业务的“计算器”

Univer内置的公式足够日常使用,但真实业务里总会遇到“标准函数算不了我自己的规则”的情况,比如按阶梯比例算提成、按工龄算年假、把数据库里的状态值翻译成中文。这时候就需要注册自定义函数。

公式引擎的注册入口大体上是向函数库中挂一个函数实现,当你拿到调用参数时,它是一个已计算好的数值数组(比如单元格区域引用会展开成数组),你只需要处理业务规则后返回结果即可。返回结果会进入Univer的计算链,依赖这个公式的其他单元格会自动联动更新。我在项目里注册过一个“工龄工资”函数,把入职日期和当前日期传入,按满一年加一百这种规则返回结果,前端表单填入职日期后,工资列立刻重新计算。整个过程没碰渲染代码,全是纯逻辑,非常好维护。

这里提醒一点:读Univer公式引擎源码时不要慌,它的函数基类封装层次比较多,但自定义函数最简单的方式是照着官方示例里的demo函数仿写一个,调用参数和返回值类型都能参照。不同版本的注册接口会有细节变化,强烈建议先看对应版本的测试用例,那比文档还准。

4.2 文件的导入导出:xlsx这条路怎么走顺

在线表格做不到和桌面Excel无缝互相打开,等于半残。Univer的生态里提供了文件导入方案,可以去解析xlsx文件生成工作簿数据。导出链路上一般做法是把Univer的JSON快照反序列化,再用成熟的xlsx序列化库生成文件。

实际跑下来,最简单的业务路径是:用户上传xlsx -> 前端解析出Univer快照 -> 渲染到表格;用户编辑完成点导出 -> 从Univer拿到快照 -> 转成blob下载。这个闭环能做,但你要对两个边界情况有预期:

  • 公式还原:Excel文件里的公式能否完整带回来,取决于解析层对公式字符串的兼容程度。实测里普通函数问题不大,极端函数和名称管理器这些高级特性可能会丢失。
  • 样式精度:合并单元格、数字格式、字体颜色的还原相对好,但条件格式这种复合规则,还原程度不够完美。如果用户对“打开必须和Excel一模一样”有执念,那目前没有哪个开源方案能做到100%保真,Univer也不例外。

4.3 前后端协同:把工作簿快照安全落库

在线表格最终要把数据保住,不能刷新就没了。Univer的工作簿整体结构可以序列化成JSON快照,接口层为我们提供了读取和写入快照的能力。我的做法是:每次用户保存请求时,前端拿到当前工作簿的快照,POST到后端存一份版本记录;需要恢复时再从后端取快照渲染回表格。

这里真正要注意的不是Univer,而是你自己的业务模型。快照里既有单元格值、样式、行列配置,也有公式定义,直接存JSON确实省事,但后续做数据分析和报表抽取会很痛苦。如果你只是想用Univer当录入前端,强烈建议不要在数据库里只存一个JSON字段——虽然开发最快,但以后想按字段统计、校验、告警时,你会发现JSON字段里啥都拿不出来。更合理的做法是定期把关键列同步到结构化表,同时保留JSON快照做版本回滚。

关于多人协同,Univer有对应的协同方案和 server 实现思路,核心就是前面说的命令同步。不过我要泼一点冷水:协同编辑不是开箱即用的黑盒,自己部署要处理鉴权、房间管理、冲突策略、消息推送这些外围工程。如果你的产品只是内部工具,几十个人同时使用的场景,先做“最后保存成功覆盖”和“编辑锁”也能活;真要做像腾讯文档那样多人光标可见的体验,投入的工程力量要翻几倍。

5. 其他表格方案对比:Univer不是唯一,也不总是最优

5.1 和自研渲染表格相比:时间是最大的成本

自研方案的优势是一切尽在掌握,性能瓶颈可以慢慢磨,交互可以做得很独特。但劣势也很明显:你要从虚拟滚动、区域选择、复制粘贴、撤销重做、公式引擎一点点写起。就算团队里有几个资深前端,把“能看的Excel”做成“应付真实业务”的阶段往往也要大半年。Univer帮你把最硬核的渲染和计算部分都解决了,你只需要在它的骨架上做定制。

当然自研还有一个优势是可控的依赖面窄,不用担心上游框架变更。但以Univer这种项目体量和社区活跃度,这种风险对大多数团队来说是远期问题,不是当下决策的主要矛盾。

5.2 和Luckysheet、Handsontable、x-spreadsheet的取舍

这四类方案我都在不同项目里用过,简单列个对比:

方案定位公式能力扩展性许可/商业友好度适用场景
Univer办公套件框架强,独立公式引擎强,插件化开源,需研究商用条件在线编辑器、办公套件、协同产品
Luckysheet表格组件中较弱,架构偏旧开源轻量在线表格功能
Handsontable数据网格组件弱,以数据操作见长中商用需授权后台管理系统数据录入、看板
x-spreadsheet轻量表格组件弱中开源极简表格、原型验证

如果你定位是后台管理的“数据网格”,比如订单列表、日志记录、字段可编辑的企业应用,Handsontable那种以数据驱动交互的思路反而更顺手;如果你要的是一个真正能跟Excel掰手腕的在线编辑体验,Univer在开源阵营里优势明显。x-spreadsheet适合已经明确需求很简单、预算也不高的项目,但别指望它支撑复杂公式和精细样式。

5.3 什么情况建议不要用Univer

有些场景我甚至不建议上Univer。第一,产品只需要表格态展示,不需要编辑能力或仅支持简单编辑,选一个轻量级数据表格组件是最优解,Univer这种规模的框架会给你带来不必要的包体积和样式冲突。第二,业务强依赖Excel的宏、VBA、ActiveX控件等桌面特有能力,Univer再怎么做也替代不了——这种需求要么考虑桌面端方案,要么从业务流程上避开。第三,团队没有TypeScript功底或不愿意读源码,深度定制Univer会变得举步维艰,插件机制虽好,但理解它还是需要门槛的。

6. 我的一些个人体会

实际用了几个月下来,我最大的感受是:Univer把Web表格的天花板顶到了一个普通自研团队很难短时间达到的位置。它不是一个“填一下就能用”的小组件,更像一片可以持续耕耘的田地,前期投入是值得的。接进去的团队,最好把它当成项目里一个长期共建的模块,而不是一次性依赖库。

最后分享一个小技巧:动手写业务之前,一定先去跑通官方文档里的几个基础示例——创建空白工作簿、导入已有数据、注册一个自定义公式。这三个跑完,你对Univer的底层逻辑基本就有手感了。不要一上来就想着做完整个在线Excel,那只会让自己陷入无穷无尽的配置细节里。从我踩过的坑来看,慢慢啃Univer的源码和示例,比到处搜问题解法要高效得多。

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

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

立即咨询