☰
开源在线表格库Univer:从架构原理到二次开发实践
2026/11/5 3:05:19 网站建设 项目流程

如果你跟我一样,在自己的后台系统里被老板一句“在网页里搞一个像Excel那样的表格”折腾过,那你大概率也把市面上能搜到的表格方案翻了个遍。最尴尬的不是找不到库,而是找到一个看着不错的库,往项目里一接,才发现要么只能展示不能编辑,要么交互体验一上手就露馅,要么商业授权费用直接劝退。直到我翻到Univer,这个开源的在线表格库,才算觉得这事儿真的有解了。

Univer是什么?一句话——它是一套基于TypeScript开发的开源在线表格解决方案,目标是在浏览器里提供接近原生Excel的操作体验,同时把渲染、公式、协同、数据模型这些能力拆成独立的模块,让你能按需拼装。也就是说,它不是一个“只能画个表格样子”的前端组件,而是一整套能承载复杂业务数据的表格引擎。

这篇文章我会从实际接入的角度,把Univer的核心设计、跑通最小示例、踩过的坑、以及二次开发的经验一次性讲透。无论你是被产品虐的前端,还是想评估技术方案的架构师,都可以拿这篇当参考。

1. Univer到底是什么,以及它凭什么值得折腾

1.1 “网页里嵌一个Excel”这事到底难在哪

先说个扎心的现实:在网页里做表格编辑,和做一个能看的数据表格,完完全全是两码事。很多项目第一步会选原生<table>去铺数据,排版是容易了,但一旦涉及单元格编辑、公式计算、区域选择、复制粘贴、撤销重做,原生表格瞬间就会变成噩梦。你写的可能不是业务代码,而是一整套“Excel重新发明计划”。

这也是为什么社区里会冒出各种在线表格方案。但它们要么停留在“电子表格显示”层面,要么是商业产品内部实现不开放。真正能在开源领域里,把“像Excel那样完整编辑”这件事做出来的项目,一直少之又少。而Univer瞄准的正是这个位置——它不只做表格渲染,还包含了公式引擎、数字格式、条件格式、协同编辑、数据透视等一系列能力。

1.2 我为什么最终选择了Univer

在选择方案的时候,我认真对比过几个主流方向,这里直接给大家一张参考表:

方案开源/授权交互能力协同/公式维护状态
SheetJS (xlsx)Apache-2.0只负责解析/生成文件,不提供编辑界面无稳定维护,但方向不同
LuckysheetMIT在线编辑可用,架构偏传统有一定公式能力官方已停止维护,作者团队转向Univer
Handsontable商业授权编辑体验好,但受限于数据表格而非“类Excel”公式/协同需商业版持续维护
UniverMIT类Excel操作,插件化架构,持续演进公式、协同均有对应模块活跃迭代

老实说,Handsontable的成熟度确实高,但商业授权费用对很多团队来说不是一个小数字。Luckysheet曾经是我比较看好的一个,但后来官方宣布停止维护,核心团队把精力全部投到了Univer上。我就是在那个节点开始认真看Univer的。

Univer最打动我的一点是它的架构思路:核心、渲染、公式、UI、协同全是独立模块。这意味着我不需要把一整个巨型组件塞进项目里,而是可以只引入我需要的部分。而且它是MIT协议,没有套壳收费的压力,拿来当自己产品的“表格底座”是可行的。

2. 核心架构拆解:Univer为什么能这么灵活

2.1 插件化设计:整个Univer是“积木搭出来的”

我第一次看Univer的源码结构时,第一反应是“这项目怎么这么碎”。但仔细理解后你就会发现,这种“碎”恰恰是它最大的优势。Univer把能力边界拆得很清楚:

  • @univerjs/core:核心数据模型、命令系统、协同骨架,不依赖具体渲染方式。
  • @univerjs/ui:UI框架层,负责工具栏、菜单、弹窗这些界面框架。
  • @univerjs/sheets:工作簿/工作表相关的数据结构和逻辑。
  • @univerjs/sheets-ui:把表格编辑的界面交互组装起来,比如选区、编辑栏、右键菜单。
  • @univerjs/engine-formula:公式引擎,负责计算链和自定义函数。
  • @univerjs/engine-render:渲染引擎,负责把表格绘制到Canvas上。
  • @univerjs/engine-numfmt:数字格式化的底层模块。

这种拆分带来的好处很实际:如果我只是想在项目里嵌入一个“能编辑后回传数据”的表格,那我完全不需要把协同相关的模块拖进来,包体体积可控。反过来,如果后续需要协同编辑,我再把协同模块挂上就行,业务代码的侵入性不大。

提示:早期版本的Univer初始化方式和现在的插件化注册方式完全不同。如果你在网上搜到比较老的示例代码,会看到一个类似new UniverSheet({ container })的写法,那已经是过去式了。现在的写法是统一创建Univer实例,然后通过registerPlugin按需注册功能模块。千万别照着旧文章直接粘代码。

2.2 命令系统:所有操作都是“可撤回”的

用过Excel的人都知道,Ctrl+Z 撤销是一个深入肌肉记忆的操作。但很多网页表格组件恰恰在撤销这件事上做得稀烂,原因很简单——它们把“界面显示”和“数据变更”耦合得太紧密,每次修改直接改DOM,根本没法回溯操作历史。

Univer的实现方式不一样。它内部有一套命令系统,所有对表格的操作,无论是输入一个值、删除一行,还是插入一个公式,都是通过“派发命令→执行命令→更新数据模型→渲染层同步”这一条链路来完成的。命令本身就是一份可序列化的数据,所以“撤销”本质上就是把逆向命令再执行一遍,而“协同”也顺理成章地变成命令同步。

这个设计对开发者极其友好。比如你在二次开发的时候想要“点击按钮清空所有格式”,不需要去遍历单元格逐个操作,只需要构造对应的命令交给Univer执行。我自己的经验是:在写Univer的改动逻辑时,先想清楚“这一操作在Excel里对应哪条命令”,然后去找对应的API,而不是直接去改某个单元格的内部状态。这样做出来的功能不仅代码更干净,而且天然支持撤销还原。

2.3 渲染引擎:Canvas上画表格,滚动不掉帧

Univer在渲染层面选择用Canvas绘制表格,而不是堆DOM节点。这样做的好处很明显:一个几千行乘几十列的表格,如果全部渲染成DOM,浏览器会卡到怀疑人生。Canvas的画法可以把可视区域之外的内容跳过不绘制,滚动时只重绘可视区域,性能损耗基本可控。

我在实际使用中感知到,Univer在普通配置的机器上滚动、编辑、框选的流畅度是可接受的。当然,如果你要在一张十万行的大表里疯狂滚动,任何前端方案都会有压力,Univer也提供了数据分片和虚拟滚动相关的机制。但坦白说,如果真的到十万行级别,更稳妥的做法是在后端做分页或者聚合计算,而不是指望前端库硬抗。

3. 从零到一:把一个能编辑的Univer表格跑起来

3.1 工程准备

Univer本身不限制框架,Vue、React、原生JS都能用,因为它本质上是操作一个DOM容器。我习惯用Vite搭一个前端项目,TypeScript是必须的,毕竟Univer本身就是TS写的,类型推导能省掉很多查文档的时间。

npm create vite@latest univer-demo -- --template vue-ts cd univer-demo npm install

Univer的安装按需来,最基础的一套至少需要核心包、表格数据包、UI包和表格UI包。公式引擎建议一起装上,因为文章里马上会用到。

npm install @univerjs/core @univerjs/sheets @univerjs/sheets-ui @univerjs/ui @univerjs/engine-formula

3.2 最小可运行代码

以我接入时的版本为例,初始化一个空白工作簿的最小代码大致是这个样子:

import { Univer, UniverInstanceType } from '@univerjs/core'; import { UniverSheetsPlugin } from '@univerjs/sheets'; import { UniverUIPlugin } from '@univerjs/ui'; import { UniverSheetsUIPlugin } from '@univerjs/sheets-ui'; import { UniverSheetsFormulaPlugin } from '@univerjs/sheets-formula'; // 创建Univer实例,传入容器和本地化配置 const univer = new Univer({ container: document.getElementById('app')!, locale: 'zhCN', }); // 按需注册插件 univer.registerPlugin(UniverUIPlugin, {}); univer.registerPlugin(UniverSheetsPlugin, {}); univer.registerPlugin(UniverSheetsUIPlugin, {}); univer.registerPlugin(UniverSheetsFormulaPlugin, {}); // 创建一个工作簿实例 const workbook = univer.createUnit(UniverInstanceType.UNIVER_SHEET, {});

这段代码本身不复杂,但第一次接入容易在版本上吃亏。Univer现在的API迭代节奏确实比较快,不同小版本之间可能都有破坏性更新。我的做法是:直接以官方仓库里最新的Vite示例代码为基准,把整个初始化代码复制过来,再一点点精简成自己需要的。别手写,别凭记忆,这是过来人的忠告。

3.3 关键配置与实际体验

跑起来之后,你会看到一个完整的在线表格界面,有工具栏、公式栏、行号列号、sheet标签页。默认配置对大多数场景已经够用了,但有几点我建议你优先调整:

第一,容器必须有明确高度。Univer不会帮你做一些极限的样式兜底,如果父容器高度是0或者没有显式设定,表格区域就会出现渲染不出来的情况。这个坑我踩过,后面会专门再讲。

第二,默认的语言配置要确认。Univer支持多语言,但不设置的话部分内置菜单可能显示成英文。在创建Univer实例时指定locale: 'zhCN',中文化就很完整。

第三,如果你只需要“纯数据录入”,可以关掉工具栏。注册UI插件时可以通过配置隐藏部分工具栏项,这能让你嵌入的表格看起来更像一个业务表单组件,而不是一套完整的Office产品。

跑通基础表格之后,我就是从这几个配置点开始逐步往业务场景靠拢的:加上公式支持、调整界面语言、控制工具栏能力、再对接数据导入导出。整个接入过程比预想中顺利,接下来的坑才是真正花时间的部分。

4. 接入Univer路上会遇到的那些坑

4.1 表格白屏:先查容器高度,再查样式

我接入Univer的第一次示例运行,就遇到了经典的“白屏”。页面打开以后,工具栏出来了,但表格区域什么都没有。第一反应是插件注册顺序不对,排查了半天,结果问题非常低级——我的容器div高度只有默认的0px。

Univer的渲染引擎需要从容器尺寸推导画布大小,容器没有高度,渲染层就画了个寂寞。解决方案是在CSS里给容器设置显式高度:

#app { width: 100%; height: 600px; }

如果表格要在弹窗里展示,还有个更隐蔽的问题:弹窗刚打开时容器可能处于隐藏状态,尺寸计算为0,等弹窗展开后表格区域依然是空白。这种场景下,建议在弹窗打开并渲染完成之后,调用Univer重新计算布局的方法,或者干脆用v-if延迟初始化表格实例,确保容器已经有真实尺寸。

4.2 版本升级导致的API变化

这是我认为Univer目前最大的“使用成本”。官方迭代速度快,但网上大量文章、博客、甚至部分教程视频用的都是旧版API。我自己就遇到过:明明照着教程写的初始化代码,控制台直接报错,原因是@univerjs/core在某次更新后改了导出方式。

给所有准备接入Univer的人一个实操建议:去官方仓库看README、看官方Demo、看Release Note,把这三样作为你的信息来源,而不是依赖搜索出来的老博客。我后来在项目里把Univer的版本号锁死,不轻易升级,除非确实需要新功能。

4.3 导入导出Excel:别高兴太早

很多同学接入Univer后第一件事就是问:“能不能导入.xlsx文件、能不能导出Excel”。当然能,但这里有个隐藏前提:Univer本身不直接解析xlsx二进制文件,你需要配合其他解析库来做文件格式的转换。

常规套路是用SheetJS类的库把Excel文件解析成JSON数组或Workbook数据,再通过Univer的API导入到表格里。导出方向则反过来,从Univer取数据,再用SheetJS生成xlsx文件下载。这样做的好处是职责分离不太容易出错,坏处是你得自己处理格式兼容的边界情况,比如合并单元格、列宽、数字格式等。如果你的业务场景对Excel文件格式还原度要求很高,建议在开发计划里给导入导出留出足够的测试时间。有个很残酷的事实:Web端能做到“数据不错、格式大体不走样”就已经算合格了,要做到和原生Excel完全一致,工程量不会小于做一个精简版Office。

4.4 协同编辑不是开箱即用

如果你是从协同编辑这个关键词搜到Univer的,我要提醒一句:Univer有协同能力,但它给的是协同编辑的机制和可扩展的服务端方案,不是说你npm安装完、两端打开同一个页面就能自动多人连线。

Univer的协同基于命令同步和文档快照机制,需要你自己实现服务端的房间管理、命令转发、冲突处理逻辑。官方有提供协同模块和示例服务,但你要做的接入工作依然不少。如果业务是“多人在同一张表上同时编辑”这种强协同需求,我建议你把服务端方案先想清楚,再决定前端要不要直接用Univer。如果只是“不同用户各自编辑自己的表格,再导出上报”,那可以先不用碰协同,专心用基础编辑能力就够了。

5. 进阶玩法:把Univer变成自己产品的“表格底座”

5.1 自定义工具栏:把产品业务逻辑做成工具栏按钮

纯用Univer默认界面的时候,它看起来就是一个“国产Excel”。但在实际业务里,你往往需要一些专属功能。比如我们当时有个需求:用户在表格里填报数据,填完以后要一键校验所有必填项。

这个功能的实现方式不复杂:拿到当前工作表的数据内容之后遍历相关区域,把空值高亮标注出来。Univer提供了一套状态和操作API,你可以直接通过注册工具栏按钮,绑定自己的校验逻辑,让结果反馈在表格上。这样用户是在“他们熟悉的表格界面里”完成操作,而不是跳到一个生硬的外部表单页面。

5.2 自定义公式:把业务计算变成表格函数

如果你只用Univer内置公式,那它就是个普通的计算表格。但Univer支持自定义函数,这意味着你可以把业务规则直接变成单元格里的公式,比如计算某个项目的报价、根据配置的规则解析金额等。

注册一个自定义函数的核心步骤在官方文档里写得很清楚,大体思路是:

  1. 继承或实现Univer规定的函数类。
  2. 定义函数名、参数个数和类型。
  3. 在normalize、calculate这类生命周期方法里写计算逻辑。

我记得我当时注册了一个类似PROJECT_COST(材料费, 工时, 倍率)的自定义函数,业务方直接在表格里拉公式算报价,再也不用跑到后台上报后再等结果返回了。这个能力一旦用上,产品的深度就完全不一样了。

5.3 数据联动:让表格和后端真正“对话”

表格不应该是孤岛。实际做业务时,我们常见的场景是:后端返回一批数据,Univer负责展示和编辑;用户编辑完以后,要把变更后的数据提交给后端。

在Univer的数据模型里进行操作时,尽量走它提供的数据接口,而不是自己维护一份“镜像数组”。比如获取单元格数据时,用官方API去读取,这样你拿到的数据一定是经过公式计算、数字格式化后的准确值,不会出现“界面上看到的是1000,提交给后端却是1000.0000001”这种诡异问题。

钱相关的字段尤其要注意。我后来在表格里处理金额数据时,都会用Univer的数字格式化把显示精度固定,同时在后端再做一次精度校正,所有计算逻辑都不依赖前端直接提交的浮点数,这套双保险让我们避免了好几次线上金额对不上的事故。

5.4 更大的想象空间:Univer不只是表格

最后提一个容易被忽略的信息:Univer团队的目标并不局限于电子表格,它同时在布局文档和幻灯片的产品形态。虽然目前大多数人关注它是因为表格,但它的底层架构是面向更广泛的文档类场景设计的。

如果你所在的公司或团队,长期在做在线文档、协同办公方向的产品,那Univer值得你长期跟踪。即使你现在只用了它的表格模块,这套架构也为以后扩展留下了可能性。我自己的体会是,选技术方案时除了看眼下需求,还要看项目的底层设计理念是否能跟着业务一起成长。Univer在这点上,和那种“一个组件打天下”的库,属于两种完全不同的物种。

最后再分享一句我的实际体会:接入Univer的过程,本质上不是在“用组件”,而是在“集成一套框架”。这意味着初期成本会比普通UI组件略高,你要理解它的命令机制、插件体系和数据流。但这些成本一旦投入进去,后面换来的收益是长期且持续的。如果你手头正好有一个在网页里编辑表格的需求,别急着自己从头造轮子,先去Univer的在线Demo里点两下试试手感——大概率你会和我一样,决定认真把它引入到项目里。

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

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

立即咨询