如果你最近在做前端项目,尤其是要给业务系统塞进去一个“能用的表格”,大概率已经听过 univer 这个名字。它在 GitHub 上的 Star 涨得很快,社区讨论也越来越热。简单说,univer 是一个开源的、基于 TypeScript 编写的一站式在线 Office 套件,覆盖在线表格、文档、幻灯片三大场景,其中最成熟、最核心的就是表格能力。我这次把它接到公司内部的数据管理后台里,跑了小半年,从选型、安装、配置到各种踩坑,一路摸下来,确实有不少值得记录的东西。这篇就围绕 univer 在线表格的集成来做一次完整复盘,给同样在调研表格方案的你一点参考。
1. 先搞清楚 univer 到底解决了什么问题
1.1 为什么在线表格这么难做
很多团队一开始觉得“不就是做个表格嘛”,真动手才发现是个无底洞。在线表格远比静态展示复杂,它需要处理选区、键盘导航、拖拽填充、单元格编辑、公式链重算、复制粘贴、条件格式、图表联动,还要兼顾大数据量下的渲染性能。传统 DOM 渲染方案在几千行数据时还能撑住,一旦到几万行,操作起来就明显卡顿,输入一个数恨不得等半秒。
我最早负责的内部运营后台,就是先用 HTML table 摆数据,后来换成一个个输入框模拟编辑,再后来引入第三方表格控件,每一步都踩到性能或者交互的坑。真正的问题在于,表格不是一个组件,而是一个拥有完整交互模型和计算模型的复杂系统。自己做代价极高,买商业组件又贵又封闭,所以开源的 univer 才会这么快被关注。
1.2 univer 的定位与全貌
univer 的定位不是“又一个开源 Excel 控件”,而是一整套可插拔的 Office 套件前端方案。项目整体包含 Univer Sheet(表格)、Univer Doc(文档)、Univer Slide(幻灯片)三块能力,底层共用一套数据模型和插件机制。它的渲染层是基于 Canvas 的,不依赖 DOM 去绘制单元格,所以大数据量下的性能表现比传统方案好得多。
从架构上看,univer 把渲染、交互、公式引擎、数据存储拆成独立模块。核心包@univerjs/core负责文档模型和依赖注入,@univerjs/sheets负责表格交互,@univerjs/engine-formula负责公式解析和执行,UI 层则通过@univerjs/design和预设组件来组织。这样的好处是,你可以缺省一些模块,或者替换掉默认工具栏,而不是被迫接受一个黑盒。
2. 选型对照:为什么我没用 Luckysheet,也没用 Handsontable
2.1 主流方案对比
在最终敲定 univer 之前,我把市面上常用的几个方案都过了一遍。下面这个对比表大概能代表我的调研结论:
| 方案 | 开源协议 | 渲染方式 | 公式引擎 | 协同支持 | 二次开发友好度 |
|---|---|---|---|---|---|
| Handsontable | 非商用免费,商用付费 | DOM | 有,但功能偏基础 | 需要商业版 | 一般,定制成本高 |
| Luckysheet | MIT | Canvas | 有,生态相对封闭 | 需要配其他服务 | 中等,文档不完整 |
| x-data-spreadsheet | MIT | Canvas | 轻量,函数少 | 不做 | 简单,但能力有限 |
| SpreadJS | 商业授权 | Canvas | 完整度高 | 提供方案 | 高,但受授权限制 |
| univer | Apache 2.0 / 自定义 | Canvas | 自研,覆盖广 | 提供基础设施 | 高,插件机制完善 |
商业方案能力确实强,但授权费用和封闭程度是我们这种中小企业比较难接受的。Luckysheet 在社区里热度不错,不过它的维护节奏和代码结构对二次开发不算太友好。Handsontable 更偏向“增强型可编辑表格”,把它当 Excel 用,交互深度不够。
2.2 我最终选 univer 的三个理由
第一是性能。univer 的 Canvas 渲染加上自研的视口计算,百万级单元格的前端操作基本不会把浏览器拖死,这在处理运营导出的大宽表时非常关键。第二是公式能力。@univerjs/engine-formula是独立的公式引擎,支持 400+ 常用函数,还有自定义函数注册机制,能直接复用 Excel 的很多表达式习惯。第三是扩展性。它的插件化设计让我可以在不侵入源码的情况下替换工具栏、增加右键菜单、监听选区变化,非常适合集成到现有业务里。
当然,选它也有代价。univer 当前版本迭代很快,API 时有变化,文档经常落后于代码,这意味着你要有跟着 changelog 走的思想准备。后面我会具体聊怎么规避这些坑。
3. 快速接入:把 univer 表格跑起来
3.1 环境准备与初始化
univer 本身不挑框架,React、Vue、原生 JS 都可以接。我这边技术栈是 React + TypeScript + Vite,所以下面的示例都以这个组合为例。Node 版本建议 18 以上,包管理器用 pnpm 或 npm 都行,npm 也没问题。
先创建一个全新的前端工程,这里用 Vite 的模板跑起来:
npm create vite@latest univer-demo -- --template react-ts cd univer-demo npm install安装核心依赖时,最省事的方式是用官方预设包:
npm install @univerjs/presets @univerjs/presets/preset-sheet提示:
@univerjs/presets是官方提供的快速集成方式,适合先跑通业务。如果你后续要深度定制 UI,建议再按需拆装@univerjs/core、@univerjs/sheets、@univerjs/engine-formula等基础包。
3.2 React 项目集成的最小代码
依赖装好之后,事情就变得很直接。先新建一个UniverSheet.tsx文件,在组件里创建 univer 实例并挂载到容器节点:
import { useEffect, useRef } from 'react'; import { Univer } from '@univerjs/presets'; import { UniverPresetSheet } from '@univerjs/presets/preset-sheet'; import '@univerjs/presets/lib/styles.css'; export default function UniverSheet() { const containerRef = useRef<HTMLDivElement>(null); const univerRef = useRef<Univer | null>(null); useEffect(() => { if (!containerRef.current) return; const univer = new Univer({ locale: 'zhCN' }); const preset = univer.use(UniverPresetSheet({ container: containerRef.current, header: true, toolbar: true, footer: true, sheet: { showGridlines: true, }, })); univerRef.current = univer; return () => { univer.dispose(); univerRef.current = null; }; }, []); return <div ref={containerRef} style={{ height: '600px', width: '100%' }} />; }这段代码做的事情其实很简单:创建 Univer 实例,把表格预设挂上去,指定容器,然后它自己就把工具栏、工作表、编辑区都渲染出来了。univer.dispose()在组件卸载时调用,用来释放内存,这点在 SPA 里特别重要,后面我也会专门提。
3.3 关键配置项说明
UniverPresetSheet的配置项不算多,但每个都有讲究。
locale: 'zhCN'决定界面语言,想要英文界面用enUS。container是必填项,指向一个已经存在于 DOM 里的元素。header、toolbar、footer控制默认 UI 部分的显隐。如果你打算自己做工具栏,就把toolbar关掉,只保留表格区。sheet.showGridlines控制是否显示网格线,做数据看板时经常关掉它。- 还有一个容易被忽略的
theme配置,可以通过它覆盖默认主题色,配合企业品牌色。
我只跑通的最小示例里,所有配置都用默认值,真正需要折腾的是后面进入业务场景时,比如导入数据、公式重算、协同编辑。
4. 核心场景实战与参数拆解
4.1 公式引擎:worker、函数注册、自定义公式
univer 的公式引擎是我最看重的部分。它通过@univerjs/engine-formula在 worker 里跑计算,不阻塞 UI 线程。当你输入=SUM(A1:A5)时,公式引擎会先做词法解析、语法解析,再构建依赖树,只有被数据变化影响的单元格才会触发重算,而不是整张表全部算一遍。
我在集成的第二步就试了自定义函数。业务里经常要用到一个“状态标签”逻辑:某一行数据的状态字段是英文编码,需要映射成中文描述。用普通函数很难一眼看明白,所以我注册了一个自定义函数:
import { FunctionBase } from '@univerjs/engine-formula'; import { serialize } from '@univerjs/core'; class StatusLabelFunction extends FunctionBase { name = 'STATUSLABEL'; calculate(...args: Array<number | string>) { const value = args[0] as string; const map: Record<string, string> = { OPEN: '待处理', CLOSED: '已关闭', PENDING: '进行中', }; return map[value] ?? value; } }注册的方式也很直观:
const formulaEngine = univer.getPlugin('formula'); formulaEngine.registerFunction(new StatusLabelFunction());注册之后,你就能在单元格里直接写=STATUSLABEL(A2),和原生函数一样使用。关键是这种能力不用改 univer 源码,完全通过公开 API 扩展,这才是它作为平台的价值。
4.2 样式与条件格式
在线表格不能只“能编辑”,还得“好看”。univer 对单元格样式支持得比较完整,包括字体、颜色、边框、背景、对齐方式、合并单元格等。我的建议是,能用数据模型来设置样式,就不要让用户手动一项项点,效率低还容易乱。
条件格式这块,univer 支持基于规则的高亮、色阶、数据条等。我做过一个请假统计表,需要把超过 3 天的记录标红,用条件格式规则设置后,数据更新时样式自动刷新,规则大概长这样:
const rule = { ranges: [{ startRow: 1, startColumn: 0, endRow: 30, endColumn: 5 }], type: 'cellIs', operator: 'greaterThan', value: 3, style: { fill: '#ffcccc', fontColor: '#cc0000', }, };如果直接用 JavaScript 手动给每个单元格设置背景色,数据量一大就会很卡。利用条件格式规则,由渲染层自己判断是否命中,性能要好得多。
4.3 数据验证与透视表
数据验证是业务系统的高频需求。比如某列只能填数字,或者只能选下拉列表中的值,univer 的setDataValidationAPI 可以做的。
我印象最深的是做“供应商信息录入表”时,要求“所在城市”列只能从预设城市列表里选。用数据验证配置后,用户点开单元格就能看到下拉选项,填写错误还会被拦截。这类交互能直接减少脏数据,省去后端很多判断逻辑。
透视表也是 univer 最近做的比较重的功能。它把聚合统计、行分组、列分组、值汇总都封装好了,数据源区域一变,透视结果会自动更新。之前我们用 JS 手写汇总逻辑,求和、计数、均值都要自己维护,换成 univer 的透视表之后,这部分工作量直接清零。
4.4 在线协同:univer在线能力边界
“univer在线”这个热词,指的是 univer 在浏览器端实现的完整在线编辑体验,但它并不等于“开箱即用的多人协同 SaaS”。univer 提供了协同编辑的基础设施,比如基于 CRDT 的冲突处理模型、操作合并、版本快照等机制,但真正要打通多人实时协同,你还需要一个服务端来做消息广播和持久化。
这块我目前只做了内部验证:通过官方文档里的协同示例,在两个浏览器标签页里打开同一份表格,编辑时能看到光标、内容和操作的变化。它底层走的是类 WebSocket 通道,服务端需要自己部署官方提供的协作 SDK 或者自己实现协议。如果你只想做在线表格,不需要多人同时编辑,那 univer 默认的单机模式已经完全够用,不需要碰协同。
提示:把 univer 当“在线表格组件”用,和把 univer 当“协同办公平台”用,是两个完全不同的落地深度。前者一天就能集成,后者要投入服务端开发,别被热词带偏预期。
5. 常见问题与排查技巧
5.1 页面白屏:样式注册与初始化顺序
白屏是很多人第一次接 univer 时遇到的头号问题。常见的原因有三个:一是样式文件没引入,二是容器没有拿到实际高度,三是初始化代码在 DOM 挂载前执行。
解决办法从三个方向排查。先确认@univerjs/presets/lib/styles.css有没有 import。再检查容器是否设了明确高度,univer 的设计是容器高度由你给定,你给个height: 0的 div,它自然渲染不出东西。最后看useEffect里是否先判断了containerRef.current,如果容器还没挂载就去初始化,页面也是白的。
我刚开始接的时候,就是因为样式路径记错,导致表格看起来像“没渲染”,其实 DOM 已经存在,只是样式没套上。这种问题 F12 打开一看就能定位,不算难。
5.2 公式不执行:worker 路径配置
公式不执行的表现是,你在单元格里输入=SUM(A1:A5),回车后结果一直是 0 或者直接显示公式原文。这种情况大概率是公式引擎的 worker 没起来。
univer 的公式计算默认跑在 worker 里,它需要能正确加载对应的 worker 文件。在 Vite 环境里,你可以通过配置 worker 路径或者在代码里显式注册 worker 来修复。我当时的做法比较土,直接把 worker 文件放到public目录,然后在初始化公式插件时指定路径:
UniverPresetSheet({ container, formula: { workerUrl: '/worker/formula-worker.js', }, })如果你用 Vite 的原生 worker 导入,也可以写成import FormulaWorker from '@univerjs/engine-formula/worker?worker'这种形式。核心就一个思路:确保浏览器能拿到 worker 脚本的最终 URL,路径别写错。
5.3 大数据量渲染优化
univer 的性能基座很好,但也不是在所有场景下都能无脑冲。我导入过一个 5 万行、20 多列的 CSV,默认设置下交互依然流畅,但做一些全列样式操作时能感觉到延迟。这时候可以从几个方向优化:
- 减少整列样式设置,尽量用条件格式或者数据模型规则。
- 关闭不必要的网格线,
showGridlines: false能减少绘制负担。 - 避免直接监听所有单元格的
change事件,只在需要时监听选区或指定区域变化。 - 大数据量首次渲染时,可以先用
sheet.setRowData批量写入,再统一触发视图刷新,不要一行行setCellValue。
实际业务里,运营上传的表格经常是几万行带合并单元格的,univer 依然能撑住,这点我是满意的。但如果你要做大几十万行的数据填报,还得再评估,毕竟重交互和轻展示的负载不一样。
5.4 与用户系统集成时的注意事项
和业务系统集成时,最容易踩的是“生命周期”和“数据同步”两个坑。生命周期指的是 univer 实例的创建和销毁要跟随页面组件的挂载和卸载,否则路由切换后,表格实例还残留在内存里,会越跑越卡。数据同步指的是 univer 内部状态和你的业务数据之间要有一套清晰的同步策略。
我的做法是,定义一个 getter 和 setter,只在用户“保存”或者“退出”时读取全表数据,不在每次单元格变化时都同步到后端。这样既减少接口调用次数,也避免高频操作时和 univer 内部渲染抢线程。另外,如果系统有“只读”需要,可以通过sheet.disableEditing()或者覆盖单元格编辑事件来实现,而不是靠 UI 蒙层挡着。
提示:univer 的版本更新很快,包名和 API 都出现过调整。我建议在
package.json里锁死精确版本号,比如"@univerjs/presets": "0.3.0",不要用^或~,否则一次小升级可能带来接口变化,排查成本很高。
6. 实操中的额外体会
6.1 一些经验与资源
跑完整个集成过程,我有一个很直观的感觉:univer 最值钱的不是那些现成 UI,而是它的数据模型和扩展机制。它允许你在“不破坏核心逻辑”的前提下,往表格里塞自己的业务逻辑,这一点对需要深度定制的团队来说是决定性的。
如果你准备上手,建议先看官方示例仓库里的sheets例子,把预设跑通,再深入研究@univerjs/engine-formula的自定义函数、@univerjs/core的命令机制。社区里能搜到的实战资料不算多,但官方 changelog 和 issue 区反而是最准确的文档,遇到问题直接去翻近期的 issue,比看旧博客有用。
6.2 一些扩展方向
我目前已经把 univer 用在了数据填报、权限配置、运营明细展示三个场景里。后续比较值得尝试的方向是把它从“表格”扩展成“表格 + 文档”的组合,做业务合同、审批流、财务对账的混合工具。毕竟 univer 本身支持文档和幻灯片的集成,这套能力放在同一个数据底座上,是很有想象空间的。
再一个,官方提到的协同基础设施值得持续关注。虽然现在我没做多人实时编辑,但等到业务真正需要把“离线表格”升级成“在线协作表格”时,univer 的模型基础已经在那里了,不至于推翻重来。
我个人在实际操作中的体会是,选 univer 不是因为它“够火”,而是因为它把在线表格这件事拆成了一个可以逐步构建的工程问题。先用预设快速跑通,再慢慢把默认 UI 换成自己的设计,接着注入公式、校验、权限这些业务逻辑,整个过程很顺。当然一定要给它留足学习和踩坑的时间,别指望完全不开源码就能搞定所有问题。如果你也正在为“网页里做一个像样的表格”头疼,univer 大概率值得花一个下午试试。