1. 重新认识 Univer:它不是又一个在线 Excel,而是可编程的表格内核
1.1 一个真实场景:后台系统里的表格组件困局
前阵一位做数据中台的朋友找我,说想找一个能在网页里直接编辑 Excel 的组件。他调研了一圈:老牌的表格库停更了,商用组件报价不便宜,网上搜到的几乎都是 demo 级的小工具。后来我把 univer 丢给他,他用了一个下午就完成了原型。今天想聊的就是 univer,以及围绕“univer 在线”这波热词背后,它到底是一款什么样的开源项目。
如果你的业务也需要在网页里做表格,不是那种简单展示一两千行数据的表格,而是要像 Excel 一样:选中区域、输入公式、条件格式、合并单元格、多 Sheet 工作簿,甚至多人在线协同编辑,那么 univer 值得你花时间深入了解。它解决的问题很具体:给开发团队一个可以嵌入自家产品的高性能表格内核,而不是逼着用户跳到另一个在线表格网站去操作。
1.2 从 Luckysheet 到 Univer:同一个团队,重写了底层
要说清楚 univer,必须先提 Luckysheet。2020 年前后,Luckysheet 几乎是开源世界里做纯前端 Excel 体验的唯一选择,很多后台项目的“在线编辑”都是基于它改的。但 Luckysheet 的问题也很明显:迭代节奏变慢,架构基于早期前端技术,深挖到单元格级定制会发现状态散落、插件接口不足。后续核心团队转入 univer 项目,从底层开始重新设计,把过去在 Luckysheet 上验证过的用户需求,用一套更现代的架构实现了出来。
Univer 在 GitHub 上以 dream-num/univer 仓库为主线,定位是“可扩展的开源办公套件”。它不是简单的表格组件了,而是把表格、文档、幻灯片都放在一套框架下面。目前团队和社区迭代的重心,尤其是两代架构重构之后,最稳定的使用形态还是 Sheets 电子表格。对我这种做业务系统集成的人来说,它最吸引人的不是又出了一个 Excel 复制品,而是换了一种可编程的接入方式。
1.3 “univer 在线”到底指什么:开源 SDK 而不是托管服务
热搜词里出现了“univer 在线”,我猜不少人是把 univer 理解成了类似腾讯文档、金山文档那样的现成产品,以为打开一个网址就能在线办公。实际情况不是这样。官方提供的在线 demo 是用于体验和效果验收的,生产环境里没有“Univer 官方云服务”让你直接使用,你拿到的是一套 npm 包和构建工具链,需要集成进自己的前端项目,协同后端也得自己搭。
这意味着选型前要想清楚:团队有没有前端工程能力,能不能接受自己维护一套较重的组件依赖。Univer 的价值不在于开箱即用,而在于它是一块很好的“地基”。你在这块地基上盖多少层楼、配什么门窗,都是可控的。这一点和商业在线文档有本质差异,也和那些“传个 Excel 文件上去在线预览”的在线工具不是一回事。
我自己的看法是,如果你需要的是一个渲染效果漂亮、性能过得去、又能控制全部交互逻辑的表格工作台,Univer 是当前开源阵营里投入产出比较高的选择。接下来我从底层原理讲起,再结合实际集成过程,把它剖开来看。
2. 底层做了哪些重活:Canvas 渲染引擎与插件化架构
2.1 为什么 DOM 表格扛不住大数据量
传统网页表格的做法很简单:用 table 标签或者 div 网格,把每个单元格渲染成 DOM 元素。一两千行没问题,到十万行的时候,哪怕只创建节点都足以让浏览器布局引擎瘫痪,更不要说还要做选区、拖拽、滚动更新、公式重算。这背后是浏览器对 DOM 节点的处理成本:节点越多,布局和重绘的耗时越长,内存占用也越来越高。
Univer 选择了 Canvas 渲染路线,这是它性能和 Luckysheet 时代有本质差别的一个核心原因。单元格不再是一对一的 DOM 节点,而是由渲染引擎在画布上统一绘制。画面呈现出来的“表格”本质上是一张持续更新的画布,你在上面看到的所有格子都是像素,不是 HTML 元素。这样即使有大量可见单元格,实际需要计算和绘制的图元也远少于 DOM 树。
2.2 视口裁剪:像摄影师一样只画镜头里的格子
Canvas 表格渲染并不是把十万行全部画出来,那样性能照样崩。它的关键是视口裁剪,也叫视口计算。表格滚动时,引擎根据滚动偏移和行列尺寸,算出当前视口内能看到哪些行、哪些列,只绘制这些格子。看不见的区域不参与绘制,等滚到那里再实时补画。
这个逻辑可以拿摄影棚做类比:导演拍一场街头戏,不需要真的盖一整个城市,只在镜头能拍到的范围内搭景就够了。镜头移动,景也跟着移动。Univer 把这种思路做进了渲染管线里,加上行高列宽缓存、单元格样式缓存,滚动时不会每次重新计算全部坐标。对于常见的数据规模,比如十万行左右的基础表格,实际操作体验在主流 Chrome 上是可以接受的。我自己实测下来,比使用纯 DOM 实现的同类表格体感要好一个量级。
2.3 core、sheets、ui 解耦:业务集成才有的自由度
Univer 的架构里有个词叫插件化,这不是营销话术,而是真的把内核和界面拆开了。core 是领域模型层,负责工作簿、工作表、单元格的数据结构以及命令系统;sheets 提供电子表格领域的扩展,比如选区、公式、合并单元格这类能力;ui 是界面层,负责工具栏、右键菜单、弹窗这些交互外观。三层之间通过明确接口通信。
对做业务集成的人来说,这个分层意味着两件事。第一,你可以不用 Univer 自己的界面,只拿内核做渲染和计算,外面包自己公司的设计语言。第二,新增一种特殊功能时,不一定要改源码,可以在自己写的插件里监听命令、处理数据。我在接入过程中体会最明显的是,UI 层升级导致的布局变化并不会影响底层已经写好的业务数据处理逻辑,这在老式表格组件里很难做到。
2.4 公式引擎与协同的底层准备
公式是表格的灵魂。Univer 的公式计算模块不是简单字符串替换,而是把单元格、公式、引用关系抽象成一份依赖图。某个单元格的值变了,引擎沿着依赖边找到所有受影响的公式,只重算这些格子,而不是全表跑一遍。这套依赖追踪机制也提升了大规模报表的性能上限。
协同编辑方面,Univer 的设计给业务方留了一条很实际的路:所有的用户操作最终会转换成可序列化的命令或变更记录,而不是直接修改 DOM。你点一下单元格、输入一个字符、拖拽一片区域,底层都记录了需要协同的增量。这意味着做多人在线编辑时,天然具备“操作可以同步给远端”的基础,剩下的是搭建通道和合并冲突的问题。这一点放到后面第 4 章细讲。
3. 本地集成实操:把 Univer 表格嵌进自己的页面
3.1 安装依赖与版本锁定的教训
Univer 的包很多,核心的几个是 @univerjs/core、@univerjs/sheets、@univerjs/ui。用 npm 或 pnpm 安装即可。但这里我先说一个自己在项目里踩过的坑:Univer 从 1.x 走向 2.x 的过程中 API 变化非常大,不同大版本的初始化方式、插件注册方式都不一样,甚至同一个 API 在小版本之间也会调整。
所以集成之前第一件事不是写代码,而是打开 package.json 把这些包的版本锁死,比如锁定当前项目已经验证的某个版本。不要用 latest 或者 ^ 号放任升级,否则某天 npm install 之后所有示例代码都可能跑不通。我见过太多人把锁版本当小事,结果升级后一个函数改名,排查一下午。Univer 的包名和版本号在 npm 上都能查得到,安装时以官方文档和示例仓库的版本对齐为准。
3.2 初始化与创建工作簿:一份可运行的骨架代码
下面是一段比较常见的初始化流程。Univer 的 API 变化快,复制前建议对照你安装版本的官方示例。以近期版本常见的形态来说,逻辑是:新建 Univer 实例,注册表格插件和 UI 插件,再创建一张工作表。
import { LocaleType, Univer, UniverInstanceType } from '@univerjs/core'; import { UniverSheetsPlugin } from '@univerjs/sheets'; import { UniverUIPlugin } from '@univerjs/ui'; const el = document.getElementById('app'); const univer = new Univer({ locale: LocaleType.ZH_CN, plugins: [ UniverUIPlugin({ container: el, }), UniverSheetsPlugin({ // 具体选项按官方文档配置,例如默认工作表数量等 }), ], }); univer.createUnit(UniverInstanceType.UNIVER_SHEET, { name: 'workbook-demo', sheetData: [], });这段代码的关键不是逐行死记,而是理解流程:先有 Univer 实例,再注册插件,最后 createUnit 创建具体文档。插件顺序一般把 UI 放前面,让界面先准备好,再把表格核心挂上去。如果你发现界面没出来,优先检查的往往是这块逻辑,而不是业务代码。
3.3 用代码写数据而不是等用户手动输入
创建了一张空表之后,下一步通常是往里面塞数据。Univer 的数据结构是按行索引、列索引组织的对象。二维数组适合人看,但它内部的 cellData 更接近 Excel 文件格式:外层是行号,内层是列号,每格是一个带有 v(value)、s(style)等字段的对象。
把后端返回的 JSON 数据转为这种结构,算是集成过程中最琐碎的一道工序。我一般会封装一个工具函数:输入二维数组,输出 cellData。转换时注意三点:首行可能是表头,需要设置加粗样式;空单元格不要塞 null;日期类型建议统一成字符串或时间戳,避免单元格类型判断不一致。这些转换做好了,后面导出 Excel 的格式就不会乱。
3.4 第一次加载最容易忽视的容器与主题配置
新手页面上最常出现“表格空白”的情况,十个里有八个是容器高度为 0。Univer 的 UI 插件需要一个可见的挂载节点,这个节点必须有确定的宽度和高度。很多人只写了一个 div 没有任何样式,表格渲染出来了但被压成一条线,看起来就像加载失败。
另外是主题和语言。locale 直接传 LocaleType.ZH_CN 可以解决右键菜单、工具栏全英文的问题。主题可以通过 UI 插件的主题选项控制,如果公司有自己的设计规范,建议在接入初期就把主色、边框色、选中色对齐,而不是等业务上线后再批量改样式,因为 Univer 的样式变量很多,后期替换成本比初期要高不少。这部分配置项在官方文档里有表格可以查到,按需覆盖即可。
4. 走向业务:数据回写、导入导出与协同编辑接入
4.1 通过事件监听把表格数据交还给业务系统
表格不是孤岛。用户把数据填完后,要能把结果交还给业务系统。Univer 的业务接入方式通常是“监听操作 -> 获取当前文档快照 -> 提交给后端”。快照就是一个描述当前工作簿状态的对象,包含所有 sheet、单元格数据、样式引用等。
获取快照的具体接口因版本不同有差异,但整体思路是一致的:通过 Univer 实例拿到当前激活的 unit,调对应的 snapshot 方法。拿到快照后,你可以整体提交,也可以先对比前后快照只提交变化的部分。我的实操建议是:把“提交”操作做成显式触发,比如用户点保存按钮时取快照;不要每个单元格输入都往后端发,否则并发和接口压力都会很难看。等确认保存后再更新本地缓存,并在界面给出保存结果反馈。
4.2 导入导出 xlsx:这是选型时最容易低估的工作
做后台系统,不可避免要处理 .xlsx 文件。Univer 有导入导出相关的插件或示例,但这里我先泼一盆冷水:文件转换是最容易低估的工作。跑通“导入一个表格、导出一个表格”的 demo 很轻松,难的是保证合并单元格、样式、列宽、数据验证、公式结果这些细节在往返过程中不丢失。
我建议在选型阶段就把“导入导出一致性”列成验收项,用三份不同复杂度的真实 Excel 文件去测试:一份纯数据,一份带样式和合并单元格,一份带公式。测完你心里就有数了。不要等到业务上线后,被用户一句“我导出的文件格式不对”打个措手不及。Univer 社区对格式兼容的讨论一直很活跃,如果你的场景对 Excel 文件兼容性要求极高,前期就需要更多测试和取舍。
4.3 协同编辑:Univer 提供内核,通道和房间你得自己搭
“univer 在线”搜索热背后,很多人是冲着在线协同来的。Univer 的架构确实为协同打了基础,命令可序列化、操作可套用这个特点让协同原型不难做,但完整的协同编辑是一项系统工程。Univer 没有自带后端服务,没有房间管理,没有 WebSocket 网关,没有数据库持久化。这些都要你在自己的后端体系里实现。
协同方案的常见路径有两条:一条基于 Operation Transformation,后端收到用户操作后负责合并和广播;另一条基于 CRDT,比如对接 Yjs,用 Y.Doc 保存文档状态,前端通过桥接层同步。两条路各有利弊,简单项目选 OT + 自建房间会更容易理解;多端实时一致性要求高并且团队有相关经验时,再考虑 CRDT。这里不展开讲具体代码,因为每个业务的后端形态都不一样,但我强烈建议把“协同只做内核,服务端自己负责”写进方案评审,别等着前端框架帮你解决一切。
4.4 一套典型的协作集成形态
如果你要评估工作量,可以参考这样一个典型形态。前端项目集成 Univer,用户操作产生变更记录后发送到后端的协同服务。协同服务负责鉴权、创建文档房间、管理多个客户端的连接、把变更广播给同一房间内的其他人。同时它还要负责把最终文档状态落到数据库,比如 MySQL 或者 Redis,保证用户刷新后能重新加载。
这套形态里,前端相对简单,因为 Univer 已经把渲染和操作模型封装好了。真正的复杂度在后端:消息顺序、冲突处理、断线重连、版本快照。很多项目都是从“写完变更就存库”起步,慢慢演进到“有排队、有版本号、有回放”的完整协同。以一个内部管理系统为例,如果只是十到二十人的小团队同时编辑,一个具备 OT 基础能力的自建服务就足够用了。千万别上来就上重型的分布式协同方案。
5. 生产环境中的坑:包体积、渲染性能与兼容性
5.1 别让首屏被表格组件拖垮
Univer 功能强大,代价是体积不小。早期接入时如果直接全量引入主包,首次加载 gzip 后体积也会有数 MB 级的表现,对后台管理系统来说不算致命,对 ToC 页面来说就会拖垮首屏指标。
我的处理方式是把 Univer 的初始化模块单独拆成一个异步 chunk,只在用户进入“打开在线表格”页面时才加载,而不是放在全局入口里。路由级懒加载能显著改善首屏加载耗时,同时配合打包工具的 chunk 拆分配置,把 univer 相关包集中到一个 chunk。再配一个加载占位图,实际体感就好很多。另外 CDN 引入时一定要固定精确版本号,不要用 latest 标签,否则有一天 CDN 上版本更新,页面直接报错。
5.2 十万行数据怎么做到不卡
Univer 的 Canvas 渲染扛得住大数据,但大数据量和流畅编辑之间还差一个姿势。首先是减少无意义的全表刷新。表格数据较大时,不要动不动 setData 整表,尤其不要用一个定时器反复刷整个工作表。其次是确认渲染引擎的视口缓存是否生效,行高、列宽尽量不频繁变化,因为这些是视口计算的基础。
我在一个财务对账单页面里塞过八万行数据,真实体验是滚动不卡,但输入单元格时如果公式多,响应会有轻微延迟。优化手段是关掉不必要的单元格事件监听,把复杂的递归公式拆成几列分步计算,有条件的话把计算类任务挪到 Web Worker 侧。记住一个原则:Canvas 解决的是“显示海量数据不崩”,而“编辑海量数据还顺滑”需要你控制好数据更新频率和计算范围。
5.3 浏览器与 iframe 的兼容性细节
如果你打算用 iframe 嵌套 Univer,有几个细节要提前处理。第一个是滚动和焦点:iframe 外的页面滚动事件可能会和表格内部滚动打架,双击单元格后键盘事件也可能先被外层页面截获。解决思路是进入表格后把外层 body 滚动锁住,离开再释放。第二个是移动端 Safari 在 iframe 里的手势表现,双指缩放和惯性滚动都会有些奇怪,如果主要用户是移动端,建议先做一轮真机测试,不要只看桌面 Chrome。
Univer 底层的 Canvas 渲染决定了对不支持 Canvas 的环境基本无解,比如企业内网里的老版本浏览器。如果用户群体无法切换浏览器,这部分场景需要单独引导到受支持的现代浏览器,而不是试图在老旧内核上做降级调试。我遇到过一次内网环境,用户浏览器版本较旧,最稳妥的方案还是引导他们使用受支持的浏览器,这一点要在项目部署文档里提前写明。
5.4 高频 issue 与对应处理
最后把我在社区和高频踩坑里看到的问题汇总一下,做个快速对照,可以少走很多弯路。
| 现象 | 常见原因 | 处理思路 |
|---|---|---|
| 表格空白或工具栏不显示 | 容器高度为 0 或插件注册顺序不对 | 检查挂载节点尺寸,调整插件注册顺序 |
| 样式看起来丢失 | 渲染缓存未被正确刷新 | 使用渲染刷新接口重新渲染,确认快照包含样式引用 |
| 公式不重算 | 依赖图没被触发或手动改值后未走引擎 | 检查单元格变更事件,是否绕过引擎直接赋值 |
| 复制粘贴格式不完整 | 版本接口未覆盖或插件未启用 | 锁版本、启用对应插件、对照官方示例 |
| 协同连接失败 | 服务端未启用跨源隔离头 | 检查 COOP 和 COEP 响应头配置,本地 mock 验证 |
| 打包体积过大 | 全量引入了未使用模块 | 路由级拆分、手动拆 chunk、按需注册插件 |
上面表里的“跨源隔离”这条尤其值得注意。部分能力需要浏览器启用 SharedArrayBuffer,也就是服务端要正确响应 COOP 和 COEP 头,否则相关能力会被浏览器禁用。这个坑在本地开发环境经常遇不到,部署到测试环境才突然出现,排查时很容易忽略。
6. 选型判断:哪些项目适合押注 Univer
6.1 我眼中适合和要慎重的场景
先说适合的。你的项目是 ERP、OA、财务系统、数据填报平台、低代码平台这类内部系统,表格是核心工作台,需要深度编辑、公式、多 Sheet、条件格式,同时你又希望保持自己的产品风格,那 Univer 是很值得考虑的地基。它的可编程性和插件化能力就是为了这种场景设计的。
要慎重的场景也有不少。如果只是数据展示,纯阅读型报表用表格组件就是给自己添负担;如果客户对 Excel 文件原生格式兼容有硬性要求,比如必须完整保留 VBA 宏、复杂图表,那 Web 端目前所有开源方案都做不到,建议引导用户用桌面 Office;如果团队里前端资源不足,也没有时间去读文档、跟进版本,一上来就接 Univer 很容易卡壳,因为它的学习曲线比老式组件要陡一些,API 变动也更频繁。
6.2 和常见表格替代方案的直接对比
很多人选型都会问 Univer 和某某方案比怎么样。我把实际使用感受列一个表,授权、维护、渲染、协同几个维度都放进去,方便做初步判断。
| 方案 | 维护状态 | 定位 | 渲染方式 | 协同能力 | 集成体验 |
|---|---|---|---|---|---|
| Univer | 活跃,迭代快 | 可编程表格内核,办公套件底座 | Canvas | 内核支持、自建服务 | 学习曲线陡、扩展性强 |
| Luckysheet | 基本停止维护 | 开源表格组件 | Canvas 与 DOM 混合 | 有演示级协同 | 上手快、二次开发深度有限 |
| x-spreadsheet | 更新缓慢 | 轻量表格组件 | Canvas | 较弱 | 简单、体系不完整 |
| Handsontable | 活跃 | 商业表格组件 | DOM | 企业版提供部分能力 | 稳定、授权成本需评估 |
这张表想表达的核心观点是:Univer 不是一个和它们抢“轻量”赛道的选手,它抢的是“要做成 Excel 但又不愿意被商业产品锁死”的赛道。如果你的需求只需要一个能编辑的表格,轻量方案可能更合适;如果你要做的是一个长期演进的产品形态,表格深度体验是刚需,Univer 的上限明显更高。
6.3 判断一个开源表格组件值不值得长期跟随
最后分享一个我自己的判断框架。第一看项目本身的社区活跃度:代码提交是否频繁、issue 是否有人回复、版本发布是否有节奏。一个停止更新的表格组件,即使现在功能完美,也会被浏览器升级慢慢淘汰。第二看架构的可扩展性:文档里有没有讲清楚插件机制、数据模型、命令系统,而不是只教你怎么调配置项。第三看官方示例的完整度:能跑通多少真实业务场景,可以从侧面反映工程质量。
按这三条再看 Univer,它在活跃度和架构方面都过了线,官方示例和文档虽然有过一段相对混乱的时期,但整体在往好的方向迭代。我个人的体会是:如果你已经决定要把 Web 表格做到一定深度,又希望代码掌控在自己手里,Univer 是当前阶段值得押注的方案。但要把版本升级纳入常规维护,每次升级前读 CHANGELOG,跑一遍自己的回归用例,把它当作一个需要持续投入的产品底座,而不是一次性接完就不管的第三方插件,这也是把开源组件接进严肃业务时最需要留的心眼。