☰
开源在线表格组件Univer:Canvas渲染与协同编辑实践
2026/11/5 4:56:33 网站建设 项目流程

这两年做中后台系统,凡是跟数据沾边的业务,我几乎都被问过同一个问题:能不能在网页里直接像 Excel 一样编辑数据,而不是每次都在输入框里敲完再上传表格?做过内部报表、资产管理、运营后台的人应该都懂——"要不要接一个在线表格"最后基本都会落到选型上。Univer 就是我在这个语境下研究最久、也实际集成过的一个开源项目。它是一套基于 Web 的办公套件,主推在线电子表格(Univer Sheet),同时把文档、幻灯片也放进同一套架构里。跟传统的 DOM 表格组件不同,Univer 用 Canvas 自研渲染引擎来绘制表格内容,核心能力包括单元格编辑、公式计算、样式设置、数据透视、XLSX 导入导出,以及面向团队的协同编辑。这篇文章不打算抄一遍官方文档,而是从一个实施者的角度,聊聊 Univer 到底适合什么场景、内部是怎么设计的、接入业务系统时有哪些坑,以及如果你正在做在线表格选型,该怎么判断它适不适合你。

1. 为什么是 Univer:在线电子表格的开源之选

1.1 它到底在解决什么问题

我见过很多团队把"表格"这个词理解得过于宽泛。中后台页面里的表格,绝大多数是展示型表格,比如 antd Table、Element Table,负责把数据列出来,顶多支持排序、筛选、分页。但业务发展到一定阶段,一定会冒出一个新需求:运营希望直接在页面上改数,财务希望临时拉一个透视表算账,产品希望用户能在网页里粘贴一段 Excel 数据进来。这个时候你需要的不是展示型表格,而是一个具备电子表格能力的"编辑器"。

Univer 解决的就是这个"编辑器"问题。它不是一个写死的业务表格,而是一个可以嵌入到现有系统里的在线办公组件。你把它当成一个独立模块引进来,它就给你一张完整的电子表格:能点选、能输入、能拖拽填充、能跨单元格引用、能设置样式、能执行公式,甚至能多人同时编辑。对企业内部系统来说,这意味着你可以把 Excel 里的操作习惯直接搬到网页上,用户不用重新学一套交互。

还有一个容易被忽视的点:Univer 把表格、文档、幻灯片统一到了同一套架构之下。虽然目前大部分人在用的是它的 Sheet 能力,但"一个核心,多种文档类型"的设计意味着,如果后续你想做在线文档编辑、在线演示,不需要再从零找另外一套方案。我在做技术选型时很看重这一点,因为它决定了这个项目未来能不能跟着业务一起长大,而不是半年后又要推倒重来。

1.2 和同类方案横向对比

在线电子表格这个领域,开源和商业方案都有不少。我在选型时重点调研过 Luckysheet、Handsontable、SpreadJS,以及 Univer,最后才定了方向。这里不是要给谁做广告,而是把当时的对比逻辑记录下来,方便你少走弯路。

Luckysheet 出现得比较早,社区口碑一度很好,交互也贴近 Excel。但它的开发节奏在近几年明显慢了下来,GitHub 上的 Issue 堆积不少,社区维护状态让很多企业不敢赌。再看它底层的实现方式,本质上还是大量操作 DOM,虽然配合虚拟滚动能撑住一定数据量,但当单元格数量到几十万、公式复杂起来之后,性能会遇到明显瓶颈。Handsontable 是一个成熟商业产品,功能完整,文档也规范,但授权费用不低,而且它的定位更偏向"数据网格",不是完整的电子表格编辑器,很多 Excel 特性需要自己实现。SpreadJS 在纯前端表格领域做得相当深,功能丰富、稳定性好,但同样是商业授权模式,价格通常需要走采购流程,这对预算敏感的中小团队并不友好。

Univer 的差异化在于三个点。第一,开源且技术栈现代化,基于 TypeScript 开发,代码可读性和扩展性都不错。第二,用 Canvas 自研渲染引擎,而不是依赖 DOM 逐格渲染,这给大数据量场景留出了更多优化空间。第三,插件体系设计得比较干净,核心、渲染、UI、公式引擎彼此解耦,你可以只保留需要的部分。当然,它也并非没有缺点。项目迭代速度很快,API 变动频繁,文档有时候跟不上代码,这需要团队有一定的源码阅读能力。如果你完全不想折腾,只想要一个"开箱即用、出了问题有厂商兜底"的方案,商业产品会更合适。

2. 核心架构与渲染原理拆解

2.1 Canvas 渲染引擎:把 10 万行数据画出来

第一次看到 Univer 的渲染方式时,我特意去翻了它的渲染引擎源码,因为"用 Canvas 画表格"这件事听起来简单,做起来有不少门道。传统表格组件每增加一行,至少会产生一组 TD 节点,每个单元格里的样式、边框、文字都会占一块 DOM 内存。我在实际项目里验证过,单纯渲染两万行、每行二十列的数据,DOM 节点数就能上万,滚动时浏览器布局和重绘压力非常大。

Univer 不这么做。它把整张表当成一幅画,通过一个自研的渲染引擎,在 Canvas 上把单元格背景、边框、文字内容、选区高亮逐层绘制出来。你滚动的时候,它不会真的创建新 DOM,而是调整视口,重新绘制当前可见区域内的内容。这就好比 DOM 方案是给每个座位安排一个真人,Canvas 方案是请一位画家在幕布上把观众画出来——前排观众站起来走了,画家只需要擦掉再补画,而不需要让整个剧院的人都动一遍。

这里面的关键机制是"视口裁剪"。Univer 渲染引擎只绘制当前视口能看到的行和列,上下左右滚动时,它会实时计算新的可视范围,只对进入视口的部分做绘制,离开视口的区域直接丢弃。配合分块绘制和脏区重绘,能把单次绘制成本控制在很小的范围。我在集成时还注意到,Univer 渲染时会读取设备的 devicePixelRatio 来调整画布缩放,避免高分屏下文字发虚。这个细节如果忘了处理,用户在 Retina 屏幕上看到的表格会非常糊,看起来像没做过适配的老年软件。

2.2 命令系统与插件机制

Univer 的架构设计里,最让我觉得"这项目有工程底子"的,是它的命令系统。用户在界面上做的任何操作——输入一个值、插入一行、改个背景色、执行一次公式计算——都会被包装成一个命令。命令不会直接去改 UI,而是先提交给核心的命令管理器,由它调用对应的执行逻辑,更新数据模型,再触发界面刷新。

这有点像你在餐厅点菜:你告诉服务员"要一盘宫保鸡丁",服务员不会直接冲进厨房抓鸡,而是把订单交给后厨调度,后厨按流程做菜,最后才端到你面前。这样做的好处非常多,撤销和重做变得很简单,因为每一步操作都有记录,可以回退;协同编辑也能复用同一套机制,因为每个操作都变成了可序列化、可传输的结构化数据,而不是一堆离散的 UI 事件。

插件机制是另一个让我下决心的点。Univer 把核心功能分层拆开,渲染引擎、UI 外壳、公式引擎、底部状态栏、右上角工具栏,都是可以独立启用或替换的。举个例子,如果你的业务根本不需要写公式,那你可以不注册公式引擎,减少不必要的打包体积和计算开销。如果你不想用官方那套默认工具栏,也可以只把核心和渲染引擎接进来,自己写一套完全贴合业务的操作面板。这种可裁剪性,在企业内部系统集成时非常宝贵。

3. 把 Univer 接入到业务系统:实操记录

3.1 最小可运行示例

先把最基础的部分跑通。Univer 的 API 迭代速度比较快,不同版本之间差异不小,下面的示例是我当时整理的最小可运行骨架,以 npm 包方式接入,适合先感受整体流程。你在实际操作时,版本对应的准确 API 要以官方文档和仓库里的 examples 为准。

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

这是最基础的一组包,分别负责核心数据模型、表格功能、UI 外壳、渲染引擎。接下去是一个简化的初始化逻辑:

import { Univer } from '@univerjs/core'; import { UniverSheetsPlugin } from '@univerjs/sheets'; import { UniverRenderEnginePlugin } from '@univerjs/engine-render'; import { UniverUIPlugin } from '@univerjs/ui'; const univer = new Univer({ theme: { primaryColor: '#409eff', }, plugins: [ UniverSheetsPlugin(), UniverRenderEnginePlugin(), UniverUIPlugin({ container: 'app' }), ], }); univer.createUnit(UniverSheetsPlugin.Sheet, { name: '示例工作表', sheetList: [ { id: 'sheet-01', name: '数据明细', rowCount: 200, columnCount: 30, }, ], });

跑起来之后,你会发现页面上出现了一个完整的电子表格外壳,包括顶部菜单栏、公式栏、行号列号、底部 Sheet 标签。这里我要提醒两句:第一,容器元素必须有明确高度,也就是#app不能是 0 高度,否则 Canvas 画布会初始化成一条线;第二,初始化脚本要在容器渲染之后执行,如果放在 DOM 挂载前,Univer 会找不到容器直接报错。我当时第一次跑就栽在这两个问题上,排查了半天才发现是 Vite 开发服务器下脚本执行顺序的问题。

3.2 数据接入与常见配置

最小示例跑通之后,最重要的事情就是把业务数据填进表格。Univer 里的核心概念是 Workbook,也就是工作簿。你可以在创建时直接传入完整的簿数据,包括多个 Sheet、每张表的行列数量、单元格值、样式、合并信息,也可以在运行时通过 API 动态修改。我们实际项目里,最常用的数据接入方式是先从后端拉一个 JSON 结构的工作簿数据,再把它传给createUnit,让表格一次性渲染出来。

这个 JSON 结构不需要太复杂,核心字段就是 sheet 列表和单元格数据。如果你是第一次接,我建议先别急着做完整的数据往返,而是用下面的方式验证一条链路:后端返回一批业务明细,前端按行列重新组织成二维数组,再用 API 写入 Univer 实例。这样你能较快看到真实数据进入表格的效果,然后逐步加上样式、公式、合并单元格这些高级内容。

还有两个高频需求值得提前了解。第一个是 XLSX 导入导出。Univer 官方提供了导入导出相关能力的插件,可以把本地的.xlsx文件解析成工作簿数据加载进来,也可以把当前表格内容导出成.xlsx文件。但要注意,官方生态对复杂样式的兼容还在持续完善,比如透视表、图表、复杂的条件格式,在不同版本上表现不稳定。如果业务场景里这些能力是核心,一定要先做一轮真实文件兼容性测试再上线。第二个是数据变更监听。你需要在用户编辑后把最新数据回传后端,这时候不要在 UI 层监听一堆零散事件,而是尽量在命令系统层面处理,等用户停止操作一段时间后再批量提取数据,能明显降低请求频次。

3.3 工具栏与菜单定制

Univer 自带一套完整的工具栏,但说实话,企业集成场景里很少会原样保留。原因很简单,内部业务的用户不需要也不应该看到不相干的功能,比如你做一个数据核对工具,就不该让用户随便插入图表或者改主题色。这种场景下,Univer 的插件式架构优势就体现出来了。

官方 UI 插件支持对菜单项做拆分和定制,你可以只保留自己需要的按钮,比如撤销、重做、字体加粗、合并单元格、冻结行。再激进一点,你完全可以在初始化时不启用官方 UI 外壳,只挂载核心和渲染引擎,然后自己写一个操作面板,通过在命令系统里提交命令来控制表格行为。这样做的好处是界面和业务风格完全统一,坏处是你要自己处理选中态、隐藏高亮这些底层 UI 状态,开发量会上去。

我的建议是:第一个版本先用官方 UI,把功能跑通,再逐步裁剪菜单;不要一开始就自研工具栏,否则你会在调试表格选区和 UI 状态同步上消耗大量时间。定制按钮时要注意,Univer 的命令名和参数在不同版本间可能会拆分成更细的命令,测试时最好把调试日志打开,观察命令层的输出,确认你调的确实是正在生效的那条命令。

4. 公式引擎与协同编辑:真正拉开差距的地方

4.1 公式引擎与自定义函数

如果说 Canvas 渲染是 Univer 的骨架,那公式引擎就是它的大脑。在线表格如果没有公式计算能力,本质上就是一个好看的 HTML 表格,价值大打折扣。Univer 的公式引擎在架构上是独立模块,它维护了单元格之间的依赖关系和计算时序,单元格内容变化时会自动找到需要重算的依赖区间,而不是全表重新跑一遍。

我在集成时被问到最多的一个问题是:"Univer 能跑 Excel 里那些复杂函数吗?"从实际使用来看,常见的 SUM、IF、VLOOKUP、TEXT、COUNTIF 这些基础函数,官方支持得很全。但如果你要用非常冷门、只在 Excel 特定版本里才有的函数,最好提前查一下支持列表,别等上线了才发现计算结果是 #NAME?。还有一个更常见的坑是循环引用。业务人员做表时,经常会不小心写一个"自己引用自己"或者"A 引用 B、B 又引用 A"的公式,Univer 会给出循环依赖提示,但不同版本的表现形式不一样,体验上还达不到 Excel 那么顺滑。

更值钱的能力是自定义函数。企业内部有很多计算公式是业务特有的,比如运费计算、绩效折算、年假天数,这些在标准函数里找不到。Univer 的插件机制允许你注册自己的公式函数,注册之后用户就能在单元格里像用内置函数一样调用它。比如我做过一个场景,需要一个函数根据入职时间和职级自动算年假,注册完成后,用户在单元格里输入=ANNUAL_LEAVE("2020-03-15", "P6"),就能直接得到结果。这个能力非常实用,因为它让业务规则长在了表格里,而不是散落在后端代码里。

4.2 协同编辑的实现路径

协同编辑是每个考虑在线表格的团队都绕不开的课题。单机版表格跑得再好,也只是把 Excel 搬到了浏览器里;真正的在线协作,指的是两个用户同时打开一张表,一个人改 A1 单元格,另一个人改 B1 单元格,双方都能实时看到对方的变化,而且数据不会互相覆盖。

Univer 在这块的设计很巧妙。前面提到,用户的所有操作都会变成结构化命令,这天然为协同提供了素材。协同服务端要做的事情,本质上是把不同客户端产生的命令收集起来,按照一定的顺序分发给所有参与的客户端,然后在各自本地执行。实现方案可以分成两条路线。第一条是使用官方提供的协同服务方案,适合不想自己从零写冲突处理逻辑的团队,稳定性和一致性更有保障。第二条是自研一套基于 WebSocket 的转发服务,客户端把命令广播给服务端,服务端按时间排序后再发给其他人。

这里我必须要泼一盆冷水:不要小看协同的复杂度。如果只是做"打字聊天式"的文字同步,相对简单;但电子表格有选区、有冻结、有滚动位置、有公式重算、有合并单元格,两个用户同时操作同一个区域时,冲突策略如果没设计好,用户的数据就会被悄悄覆盖。我见过有团队用一个共享 JSON 文件加上前后端全量传输的方式实现"伪协同",结果两个人同时输数据时互相覆盖,最后只能在数据库里做乐观锁,体验非常糟糕。如果你不想踩同样的坑,建议先想清楚产品对"实时"的要求有多高。有时候"手动保存加锁"比不成熟的实时协同更可靠。

5. 常见问题与排查技巧实录

5.1 高频问题速查表

把我在接入和试运行时遇到的高频问题整理成一张表,先给结论,再逐条解释。

问题现象常见原因解决思路
页面白屏或画布高度为 0容器没有高度,或初始化时机太早给容器设置明确高度,确保 DOM 挂载后再初始化
高分屏下文字发虚Canvas 未适配 devicePixelRatio检查渲染引擎的 DPR 设置,必要时手动传入
大文件导入后滚动卡顿数据量过大,未使用分页加载限制单次渲染行数,分批次加载数据
公式不计算或结果不变依赖关系未刷新,或注册了同名自定义函数查看公式引擎日志,刷新计算链
自定义函数返回 #NAME?函数插件未注册或注册时机不对在创建 Univer 实例前完成函数注册
导入 xlsx 后样式丢失官方对复杂样式兼容有限提前做文件兼容性测试,评估导出兜底方案
两个用户同时编辑互相覆盖缺少完整的协同服务端引入版本控制,或升级为真正的协同方案

这里面最值得展开的是自定义函数不生效的问题。Univer 的公式引擎里,函数是通过注册表管理的,如果注册的时机晚于工作簿初始化,公式计算时查不到这个函数,就会直接报错。我当时第一次写自定义函数,把它放在初始化之后才注册,结果表格里所有调用这个函数的单元格都显示 #NAME?。解决方式是把注册逻辑提前,确保公式引擎在解析单元格时已经认识这个函数。还有一个类似的问题,是不同插件里定义了同名函数,后者覆盖前者,导致计算结果完全对不上。排查这类问题,最好的办法是在公式引擎的调试日志里看函数解析过程。

另一个要提醒的是数据回传的边界。Univer 作为一个前端组件,它本身不关心你的数据是存在 MySQL 还是 MongoDB。用户改完单元格之后,你需要自己决定在什么时候、以什么粒度把数据提交给后端。我见过一些团队监听每个单元格事件,然后每个事件发一次请求,数据库压力巨大,而且容易出现后发请求覆盖先发请求的问题。更稳妥的做法是采用"操作日志"思路:本地积累一段时间的变更命令,统一打包提交;后端校验版本号,通过后才真正落库。这样既减少了请求量,也降低了并发冲突的概率。

5.2 值得注意的体验优化

接入 Univer 时,除了功能和稳定性,体验上的细节也会直接影响用户接受度。不要只在技术群里讨论渲染性能,真正每天对着表格敲数据的业务用户,对卡顿和交互异常是非常敏感的。

第一个优化点是初始化速度。如果你的表格数据量大,比如单表几万行,一次全部塞给 Univer 会让首屏加载非常慢。我建议做分层加载策略:后端接口先返回前几百行,让用户看到表格已经打开,后续的数据通过滚动懒加载异步补上。Univer 的数据模型更新是动态的,你可以在用户滚动到接近底部时再请求下一批数据,体验上非常接近本地 Excel 的顺滑感。

第二个优化点是主题和语言。Univer 默认的界面风格偏国际化,英文菜单占多数,但企业内部基本都是中文用户。官方提供了语言包,可以在初始化时设置成中文。如果你发现某些菜单还是英文,大概率是语言包没完全覆盖当前版本,需要手动补充。主题定制方面,我建议跟公司设计规范对齐,开一个专门的样式配置,把主题色、字体统一起来,不要让默认蓝色块出现在内部系统里显得格格不入。

第三个优化点是权限控制。业务系统里,不是所有用户都应该能编辑所有单元格。Univer 提供单元格级别的锁定配置,你可以结合登录用户的角色,决定哪些区域只读、哪些区域可编辑。但这种前端约束只能解决体验问题,真正的安全边界一定要在后端校验。我在项目里见过只靠前端设只读的案例,用户用控制台改一下配置就能绕过限制,这种风险要提前规避。

最后想分享一个调试技巧:集成 Univer 时,建议把命令系统日志打开,让控制台输出用户每次操作对应的命令名和参数。你不需要理解每一条命令的底层含义,但配合官方文档,几乎能定位所有"操作不生效"的问题。这比在 UI 层盲猜要高效得多。尤其当你开始做插件和自定义工具栏后,这套日志几乎是必备的调试工具。

从我个人的实际体会来说,Univer 给我的最大印象是"这项目不是为了做演示而生的"。它的架构设计、命令系统、插件机制,都是奔着长期演进去的。但它也确实不是那种装上就能完美贴合业务的组件,API 的快速迭代、文档的相对滞后、复杂样式兼容的不稳定,都是你在做技术选型时要掂量的成本。如果你团队里有一个愿意啃源码的工程师,Univer 能给你带来的自由度,是很多商业产品给不了的。而如果你的业务只需要简单展示和编辑,也不追求协同和二次开发,那一个成熟稳定、有厂商持续维护的商业表格组件可能是更省心的选择。

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

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

立即咨询