Univer 这个名字,最近在技术圈子里的出镜率越来越高。如果你正在做数据处理、在线办公、低代码平台这类产品,大概率已经刷到过它了——一个基于 TypeScript 的开源办公套件,号称把 Excel、Doc、Slide 全部塞进你的 Web 应用里。我第一次在 GitHub 上看到它的时候,第一反应是不就是又一个表格组件吗,但真正跑起来之后,我承认这东西的野心比我想象的大得多。
这篇文章不是官方文档的复读机。我会按照我自己从调研、集成到二次开发的完整路径,把 Univer 是什么、能解决什么问题、怎么上手、有哪些坑,全部摊开来讲清楚。不管你是一个技术选型中的前端负责人,还是准备把在线表格嵌入自己业务的开发者,这篇文章应该能帮你省下不少蹚坑的时间。
1. 项目定位与核心价值拆解
1.1 它到底解决了什么痛点
在线表格这个领域,市面上不是没有成熟方案。国外有 Google Sheets 这种巨头,国内有各种 SaaS 产品,前端层面也有 SheetJS、Handsontable、Luckysheet 这些老牌组件。但如果你真的做过企业级应用,你会发现一个很尴尬的现状:
要么你用的是纯展示型方案,比如 SheetJS,导出导入强,但交互编辑能力约等于零,用户想在网页里点一下单元格然后改数据,它做不到。要么你用交互型组件,但这些组件往往只解决"表格"这一个点,当你需要一份完整的在线文档、一个能在同一个体系内处理表格和演示稿的场景,它们就接不上了。
Univer 的定位恰好卡在这个位置。它不是单纯做一个电子表格,而是一个完整的多文档办公套件基础框架。你用一套架构,可以同时拿到 Sheet、Doc、Slide 三种文档类型的能力,而且这几种类型之间不是拼凑的,是真正的统一数据模型、统一渲染引擎、统一插件体系。对业务方来说,这意味着你不需要分别集成三个不同的组件再想办法让它们协同。
我在实际项目里遇到的最典型场景是:客户的业务系统需要生成报价单,里面既有结构化数据表格,又有大段的说明文档,还需要一个简单的演示页给领导汇报。用传统方案,我得找三个库来拼,表格选一个库、富文本选一个库、幻灯片要么截图要么自研。而 Univer 一套框架全搞定,而且体验是统一的。
1.2 核心特性盘点
Univer 的特性列表非常长,我只挑几个对技术选型有决定权影响的说:
第一,跨端能力。它不仅仅是一个 Web React 组件,底层设计上已经考虑了移动端和桌面端场景。同一套数据模型,可以在不同端上保持一致状态。当然,目前 Web 端是最成熟的,但这套兼容性思路让它在架构上比一堆只盯着浏览器的组件明显有远见。
第二,协同编辑原生化。在线文档如果没有协同,价值减一半。Univer 的协同不是后期打补丁塞进来的,而是数据模型层面就支持的操作转换机制。不需要你自己去设计冲突合并策略,框架层面已经帮你考虑了。
第三,无头模式。这是很多开发者容易忽略的一个点。Univer 可以把渲染层和数据层解耦,你甚至可以不用它的 UI,只通过 API 操作数据模型,然后在自己设计的界面上展示。这意味着对于重度定制需求,你不需要去改别人的源码——直接换个壳就行。
第四,公式引擎。Univer 内置了一个相当完整的公式系统,官方宣称支持 300+ 公式,包括数组公式、动态数组、循环引用等。而且这个公式引擎也是独立模块,你可以单独把它跑在 Node 环境做后端的公式计算服务。
1.3 决策者关心的隐性价值
如果只是"功能多",不足以构成选型理由。我自己的判断是,Univer 的长期价值在于它的技术栈是现代的、开放的。
整个项目用 TypeScript 开发,模块化的架构使得代码的可读性和可维护性比多数同类开源项目高一个水准。而且它选择了 Apache 2.0 协议,这对商业公司来说几乎就是无法律负担——你可以放心把它嵌进自己的商业化产品里,不需要担心 GPL 那种传染性的协议风险。
这一点上我多说一句。做技术选型,尤其是给公司做选型,第一件事不是看 star 数量,而是看 License。很多好用的开源组件死于 GPL,商业法务根本不让你用。Apache 2.0 的意思是:你可以用,可以改,可以闭源,只要保留版权声明。对商业集成来说,这是最友好的协议类型。
再从社区角度说,这个项目在 GitHub 上有相当高的活跃度,而且背后的团队不是纯社区驱动的玩票项目,而是有商业公司在持续投入的企业级项目。这种"商业驱动 + 开源核心"的模式,通常意味着项目不会突然断更,bug 修复和维护周期有保障。
2. 技术架构与核心原理解析
2.1 分层架构一图流
说 Univer 之前,先整理一下它的总体认知模型。我初次接触的时候,最被惊讶到的是它的分层设计逻辑。从上到下大概是这样:
UI 层是最顶端的,它提供编辑器界面,包括工具栏、状态栏、右键菜单这些。这一层是可替换的,你可以完全用这套 UI,也可以定制,甚至完全抛弃。
中间是业务逻辑层,包含命令系统、协同系统、公式引擎、插件系统。这一层处理用户操作产生的所有业务逻辑,比如你输入一个公式、它如何被解析、如何触达其他单元格重算。
最底层是数据模型层,负责数据结构的存储与序列化。Univer 在这里用的是 Unit 的概念,一份文档就是一个 Unit,里面保存了所有单元格的内容、样式、公式、图表等完整状态。
这有个最核心的设计用意:数据模型与 UI 解耦。你可以通过一套纯 TypeScript 的数据结构表达完整个文档的状态,UI 只是把这个数据结构渲染出来。这带来什么好处?好处太多了。协同的时候你只需要同步数据结构的变化;测试的时候可以直接断言数据结构而不用驱动浏览器;导出导入的时候本质上是数据序列化与反序列化的逻辑替换。
2.2 渲染引擎:为什么网格滚动不卡
表格类的应用,性能瓶颈往往不是数据量,而是 DOM 数量。一个 100 万行的表格,如果你真的把它们全部挂到 DOM 上,浏览器直接崩溃。
Univer 的渲染引擎选择了一个很硬核的方案:Canvas 渲染。整个表格区域,本质上是绘制在一个或多个 Canvas 图层上面的,核心的网格区域只有一个 Canvas,里面任何一行一列的内容都是画上去的,而不是一个个 DOM 元素。
这就相当于你面前有一块巨大的画布,而不是一万块小拼图。不管下面挂了多大数据量的表格,在屏幕上你需要关注的永远只有你当前这个视口能看到的那几行几列。滚动的时候,视口变化,重绘速度极快。实测下来,即使在百万级数据场景下,滚动和操作的反应速度也能保持流畅。
当然,Canvas 也不是没有问题。它会带来一个经典的代价:单元格内的元素不能被 DOM 定位,比如你要在表格里嵌一个按钮,让它随单元格位置移动,就必须自己处理 Canvas 坐标转换。这个问题后面在踩坑环节我会展开说。
2.3 命令系统与撤销重做的实现逻辑
还有一个我特别欣赏的设计,是 Univer 把一切用户操作都建模成了 Command(命令)。
你在表格里输入一个值,底层是执行了一个 SetRangeValueCommand;你删除一行,是一个 RemoveRowCommand;你改变单元格颜色,是一个 SetStyleCommand。每个命令都声明注册,且每个命令都有对应的 undo 和 redo 逻辑。
回想一下,传统前端开发里撤销重做有多麻烦吧。你需要自己维护状态快照,或者记录操作序列的逆操作。而且稍有不慎,撤销重做就会和协同编辑产生冲突。
Univer 的这种命令模式天然就是撤销重做的完美解决方案。一个操作对应一个可逆的命令,用户点 Ctrl+Z,本质上就是取命令栈里最后一个未撤销的命令执行它的 undo 逻辑。而面向协同场景,操作转换就是一种对命令的变换:别人发了 InsertRow,你恰好也发了 InsertRow,合并或调整参数规则,就能保证两端看到的结果一致。
这意味着,如果你需要在业务里做"操作审计"或"操作回放"功能,这套架构是自带的优点——你记录的每一个用户操作都是一个命令对象,可以直接序列化存储,需要回放时逐条执行即可。
2.4 插件系统:为什么说它是万能扩展点
Univer 的插件机制,本质上是一个依赖注入容器。每个插件都是一个模块,它可以注册命令、注册菜单项、监听事件、访问数据模型、挂载到 UI 区域。
我用一个自己实际做过的例子来说明。我的业务需要一个"批量填充"功能:用户选中一片区域,填入一个起始值,要求按等差序列自动填充。这需求在原生 Excel 里是基本功能,但 Univer 的 UI 上可能没有直接的入口。
这时候我写了第一个自定义插件,大约 300 行代码:
安装插件的三部曲:
- 定义一个插件类,继承自
UniverPlugin。 - 在插件里监听某个快捷键或注册一个菜单项入口,用来响应用户动作。
- 在插件的执行函数里,调用数据模型 API 获取选中范围,循环计算得到序列,再把数据写回模型。
整个过程不需要改一行 Univer 的源码。发布后,用户在表格的右键菜单里就能看到"等差填充"入口,点击生效,还能正常撤销。
这要放在传统组件里,只有一个可能:你需要去 fork 源码,然后魔改内部逻辑。生产环境维护一个自维护的 fork 分支,是很痛苦的。
2.5 协同编辑的核心机制简述
协同编辑是 Univer 的一个招牌能力,简单讲一下它背后的运作原理。
Univer 的协同使用了 OT(Operational Transformation,操作变换)算法体系。每个参与协同的客户端都维护一份独立的数据副本,用户在本地执行操作后,操作会以命令的形式发送到服务端,服务端经过转换后广播给其他客户端。
这里的关键字是"转换"。比如 A 在位置 10 插入了一行,B 在位置 5 删除了一行。A 的操作广播给 B 的时候,B 本地的数据已经少了第 5 行,如果直接执行"在位置 10 插入",那插入的位置就错了。OT 算法就负责把这个操作转换为"等价的、但适用于 B 当前数据状态的"操作,经过变换后变成在位置 9 插入。这样保持了各端最终一致。
对业务开发来说,你要做的核心事情是搭建一个 WebSocket 服务端来中转这些命令数据。Univer 官方有相关的 SDK 和服务端示例工程,我自己搭建的时候实际上没写多少核心同步逻辑,主要是做权限校验和连接管理。
3. 快速集成与实操过程
3.1 环境准备与依赖安装
先把话放前面:Univer 目前最适合的宿主环境是 React + Vite 或 React + CRA。如果你用的是 Vue,官方也有支持,但我在 Vue 生态下的集成经验没有 React 那么顺滑,所以这一节我以 React 为主。
创建一个新的 React 项目:
npm create vite@latest my-univer-app -- --template react-ts cd my-univer-app npm install接下来安装 Univer 的核心包。需要注意版本,我这里用的是目前稳定的 0.x 系列,后面 API 如果调整,以官方文档为准。
npm install @univerjs/presets @univerjs/design @univerjs/preset-sheet @univerjs/core除了基本包,你通常还需要一堆基础插件依赖,比如公式、条件格式、表格操作等。最简单的方式是把官方的全功能预设集一整包拉进来,这样不用一个个挑依赖。
npm install @univerjs/presets3.2 最简初始化示例
安装完成之后,在 React 组件里创建一个 Univer 实例,并且挂载一个 Sheet 单元。代码非常直接:
import { Univer } from '@univerjs/presets'; import { SheetPreset } from '@univerjs/preset-sheet'; import "@univerjs/design"; import "@univerjs/preset-sheet/lib/styles.css"; const App = () => { const containerRef = useRef<HTMLDivElement>(null); const univerRef = useRef<Univer>(); useEffect(() => { if (!containerRef.current) return; const univer = new Univer({ locale: 'zhCN', presets: [ new SheetPreset({ container: containerRef.current, initialData: { sheets: [ { id: 'sheet1', name: '数据总表', cellData: { "0": { "0": { v: '名称' }, "1": { v: '数量' }, "2": { v: '单价' }, }, }, }, ], }, }), ], }); univerRef.current = univer; return () => { univer.dispose(); }; }, []); return <div ref={containerRef} style={{ width: '100%', height: '600px' }} />; };有一点尤其要提醒:卸载时一定要调用dispose(),否则会有监听器和定时器泄漏。在 React 严格模式的前后两次 effect 执行中,如果忘掉这一步,你会遇到典型的"Univer has been already disposed"报错。
3.3 数据模型与 API 操作
Univer 把对数据的操作都抽象成 API 和命令。我给大家一个实用场景:初始化完成后,如何通过代码向单元格写入数据。
最常见的操作方式,是通过univer.getActiveUnit()拿到当前活动的 Sheet 单元实例,然后调用它的 API:
const unit = univer.getActiveUnit(); const sheet = unit.getActiveSheet(); // 获取指定单元格的值 const cell = sheet.getCellRaw(0, 0); console.log(cell?.v); // 设置指定单元格的值,然后触发重算与重绘 sheet.setCellRaw(0, 0, { v: 'Hello Univer' }); // 合并单元格 sheet.merge(0, 0, 3, 1); // 设置整列宽度 sheet.setColWidth(0, 30);这里顺便说一下数据格式。Univer 的单元格数据cellData是一个二维映射,外层 key 是行索引,内层 key 是列索引,都是数字。每个单元格对象里v是值,s是样式 id,还有t是数据类型。这套结构跨端通用,和 Excel 的 XML 格式不同,更接近内存模型,效率高得多。
3.4 服务端渲染与无头模式
如果你需要在 Node 环境批量生成 Excel 文件,或者想在后端计算时获得与前端一致的公式结果,服务端直接引入 Univer 数据模型是能满足需求的。
这里需要提一个概念:Univer 的"无头模式"(headless mode)。也就是说,你可以不创建 DOM 容器,而是直接创建一个工作簿实例,加载数据,执行命令,再导出数据。
import { Univer } from '@univerjs/presets'; import { SheetPreset } from '@univerjs/preset-sheet'; import { UniverSheetsAPI } from '@univerjs/sheets'; const univer = new Univer({ presets: [new SheetPreset()], // 不传 container,进入无头模式 }); const unit = univer.createUnit( SheetPreset.createUnitData({ sheets: [{ id: 'sheet1', name: 'Sheet1', cellData: { "0": { "0": { v: 1 }, "1": { v: 3 } }, "1": { "0": { v: 2 }, "1": { v: 4 } }, }}], }) );然后在 Node 环境同样可以调用公式计算:
const sheet = unit.getActiveSheet(); sheet.setCellRaw(2, 0, { v: '=SUM(A1:B2)', t: 1, f: 'SUM(A1:B2)' }); const result = sheet.getCellRaw(2, 0); console.log(result?.v); // 10这个东西的实际价值很大。比如你的业务需要依据模板生成表单,后端通过无头模式算出结果后,再直接导出为 Excel 下载。这个链路完全可以不做任何浏览器参与,纯 Node 跑完,稳定性强得多。
4. 常见问题与排查技巧实录
4.1 常见问题速查表
集成过程中最浪费时间的往往不是大架构问题,而是一堆小坑。我把实测下来容易踩的几个整理成表格:
| 问题 | 现象 | 原因 | 解决方式 |
|---|---|---|---|
| 样式丢失 | 初始化后表格样式正常但工具栏空白 | 未引入 design 包或 resizer 样式 | 确认@univerjs/design被引入 |
| 重复初始化报错 | React StrictMode 下报 already disposed | 卸载时未正确 dispose | 在 effect cleanup 里调用univer.dispose() |
| 主题不一致 | 表格深色主题与业务站点风格冲突 | 默认主题是品牌色深色主题 | 通过theme配置项覆盖主题 token |
| 字体大小错乱 | 单元格字体显示异常但数据正常 | Canvas 渲染未正确加载字体资源 | 确保 Web 字体加载完成后再创建实例 |
| 单元格无法聚焦编辑 | 点击单元格不能直接输入 | 编辑模式需要聚焦 fast edit 模式 | 检查 selection 配置和allowEditing选项 |
4.2 单元格区域定位的经典坑
之前我说到 Canvas 渲染带来的一个副作用:所有 DOM 元素都无法附着在表格区域内部。如果你需要做类似于 Excel 的数据验证下拉箭头、批注小图标、或自定义悬浮按钮,顺着表格滚动移动位置,这一块是绕不开的难点。
我自己做"单元格批注"功能时,最开始想的是在单元格上叠加一个绝对定位 div。滚动后体验非常怪异,div 不跟随 Canvas 视口移动,错位严重。
最后采用的方案是:在独立的图层上绘制。Univer 允许你通过插件注册自定义渲染图层(overlay layer),每一帧直接绘制 Canvas 图形而不是使用 DOM。具体的做法是在插件生命周期里获取 render engine 引用,注册自己的渲染器,每帧同步画布位置和单元格世界坐标。听上去复杂,但代码量其实可控,而且性能比 DOM 方案稳定得多。
提示:涉及 Canvas 场景,坐标转换一定要用引擎提供的 API。不要自行通过 offset 计算,不同平台 DPI 和缩放系数会导致你计算的结果偏差。
4.3 大数据量性能调优
有同事在测试环境整合数据时卡得明显。排查下来发现核心原因:他把 10 万行数据一次性写入 cellData 后初始化,渲染没问题,但每次滚动和操作都会触发全量变更事件。其实 Univer 有增量渲染和虚拟滚动机制,但对大数据的承载方式仍然有规律。
总结几条实测调优策略:
第一,初始化时可以分块加载数据。不要一次性塞 10 万行完整的单元格数据,可以先用一个rowCount = 100000的稀疏矩阵,让表格虚拟结构存在,但内容按视口延迟加载。
第二,避免大量细粒度样式对象。假设你给每个单元格都设置一个独立的样式 ID(如字体颜色、边框),会产生大量样式对象,此时渲染的哈希计算和内存占用都会上涨。最佳实践是:尽量使用整列/整行的统一样式,而不是每个单元格单独定义样式。
第三,尽量少用setCellRaw逐个写值。如果需要进行批量导入,采用命令SetRangeValuesCommand一次性提交区域数据。每次命令提交后都会有变更计算,不要小看逐个调用的累积效应。
4.4 公式不重算的触发条件
有次我遇到一个问题:单元格公式在前端输入后,结果正常显示,但我通过 API 在别的地方修改了它的依赖单元格,结果却没有更新。查了很久才发现,API 修改数据时默认不会触发公式链路的重算。
解决方式是给修改命令消息加上公式重新计算的标识,或者在业务逻辑里主动调用unit.getActiveSheet().recalculate()。所以依赖数据模型的托管写入,在业务代码里一定要检查是否要斩断缓存链路并重新计算。
同理,如果你编程式写入的数据量很大,重算一次的开销也不小。合理思路是:先调用一下数据写入接口,在你准备完成之后,显式地调用一次重算,而不让每次变更触发多次重算。
4.5 国际化与中英文混排
Univer 初始化时通过 locale 参数设置语言,但现在业务里经常要允许用户动态切换。有些版本里 locale 是实例创建时候定死的,运行时再切换需要重建实例,体验很差。
我的经验是:如果你有多语言切换需求,务必要在比较早的阶段就将 Univer 的 UI 文案纳入系统的多语言包,并且规划好"切换语言 == 重建 Univer"的兜底逻辑,在用户切换语言后重新加载,但保留当前文档数据。这是最稳妥的方案,不要幻想着无缝热切换——Univer 内部大量文案是插件渲染时读的,强行替换容易遇到只改半截语言的问题。
另一个常见问题是中英文混排的换行与截断。Univer 基于 Canvas 文本时,对中文和英文的宽度计算是分开处理的,如果表格列宽不够,中文会比较多地出现截断。实践中建议给带表格列设置一个合理的 autoWidth 规则,或者在导出数据时统一检查列宽。
4.6 版本升级的破坏性变更
我要在这里非常诚恳地提醒大家:Univer 的 0.x 时代,版本间破坏性变更极多。它的 API 演进很快,你在网上搜到的很多示例代码可能和你当前安装的包版本根本不兼容。
具体到实践中,我的建议是:
- 锁定精确版本号,不要用
^0.x这种范围版本,否则一个npm install可能直接破坏应用。 - 升级时一定看官方发布的 CHANGELOG,别想当然认为小版本是向后兼容的。
- 遇到类型报错,以当前版本的类型定义文件为准,不要强行
any糊掉,否则后续维护会很痛。
我自己的项目在从 0.1 升到 0.2 时,就经历了命令消息结构、注册方式、插件生命周期接口的全面调整,所有的业务插件几乎要重写一遍。所以如果你准备把 Univer 用在长期维护的项目中,提前把这个版本变更成本写进技术方案里。
4.7 与后端系统的数据交换
很多同学会问,Univer 的表格数据怎么和我的后端数据库做同步。这里并没有一个官方的"自动同步"按钮,你需要自己设计交换方案。
比较成熟的架构是:前端初始化时从后端读取 JSON 数据,转换成 Univer 的 cellData 结构,渲染到表格中。用户修改后,前端收集变更命令,通过 API 批量提交到后端。后端负责把变更内容写入数据库,并广播给其他协作者。
这里的关键点是,同步的单位不是整个工作簿,而应该是一个个命令。我做项目的时候设计了一套简单的协议格式:
{ "rev": 12, "command": "setRangeValues", "params": { "row": 0, "col": 0, "rows": 1, "cols": 1, "value": "新数据" } }后端接收到命令后,可以并行处理多个客户端的命令,做简单的 OT 或队列化处理。使用命令作为最小同步单元,比整个工作簿同步高效得多,也是 Univer 官方推荐的模式。
如果只想做简单的"保存读取",也可以直接读整个工作簿 JSON 存后端,但要注意数据量。10 万行数据序列化后至少有几十 MB,频繁全量保存会拖垮整个接口,仅适合小规模管理后台使用,不建议做企业级并发方案。
5. 业务场景落地与生态整合
5.1 在线报表与数据录入系统
第一批把 Univer 用进生产环境的场景,大多是在线报表和数据采集系统。
传统思路是后端生成 Excel 模板,用户下载填好了再上传,系统解析。这一套流程的痛点很明显:用户本地装不装 Office 就够磨人了,上传后格式解析还要大量兼容工作,做各种容错处理。
用 Univer 之后,这个流程可以完全 Web 化:后端下发 JSON 模板,前端用 Univer 渲染成可编辑表格,用户在线填写提交,数据直接以 JSON 存储入库。全链路没有一次文件上传下载,没有本地软件依赖,体验和效率都提升一个量级。
这里有一个很实用的模式:结合表单 UI,让 Univer 做数据编辑的专业入口。左侧是普通表单组件,右侧是 Univer 表格,用户可以在表单里输入,在表格里实时看到结构化预览。它们之间通过 command 同步数据,保证状态一致。
5.2 低代码平台中的表格能力
现在低代码平台是各个企业数字化的热门方向,而表格组件是低代码平台中几乎必配的能力。Univer 在这里的优势是它的插件化让低代码平台能做得非常深。
比如低代码平台需要拖拽组件到画布,这时 Univer 可以作为一个组件被嵌入。画布外的配置面板修改单元格样式时,通过 API 调用 Univer 的 setStyle 命令即可。用户在表格里输入数据,也可以通过事件回调同步到低代码平台的数据模型。
另一个常用的扩展是"公式"在低代码中的价值。业务用户可以在表格里写公式,平台不需要自己实现计算逻辑,Univer 的公式引擎全权接管。甚至有些低代码平台直接把 Univer 当作可视化公式编辑器,先在表格里配好计算公式,再换算成业务规则。
5.3 教育录题与题库系统
还有一个我觉得未来空间很大的方向:教育领域的题库系统和在线阅卷。
Univer 天然适合做数学公式、化学方程式、金融表格这类富结构化内容的录入和展示。比如录数学题时,你可以在 Univer 单元格里插入富文本和公式,导出成结构化数据,存到题库里。做题时前端拉取题目,Univer 负责把公式渲染出来。由于它的 Canvas 渲染能力强,在手机端也能有比较流畅的缩放和滚动体验。
这种场景下最给力的是:单元格里可以内嵌图片、图表,甚至可以触发展示交互。这意味着你可以在单元格内做一个小型的可视化分析区域,而不需要单独嵌入 ECharts 之类的组件。Univer 自带原生图表插件,在支持的类型上、交互反馈上,比传统图表嵌入表格组件要顺畅很多。
5.4 从表格到全办公生态的演进
Univer 的更大想象空间在于,它的架构是文档型的,而不只是表格型。官方在官网和 GitHub 上都强调了 Doc 和 Slide 的支持能力。
这意味着未来如果你需要在一个产品里同时做在线文档、在线表格、在线演示三大工种,并且让它们的数据互通,Univer 是少数能从底层就完成这件事的开源项目。你可以把一份数据,以表格形式露出给一个用户,以文档形式汇报给另一个用户,改动端实时联动。
我在规划自己业务线的下一阶段时,已经把"从纯粹的表格工具升级为全套在线文档基础设施"这个方向放进了 roadmap。一旦这条链路跑通,处理复杂办公场景的能力会是传统选型方案难以比肩的。
5.5 商业选型时的成本和风险
最后必须要说一点现实的东西:选型和上生产,是两回事。
如果你只是做技术验证,那 Univer 的学习成本并不高,一两天跑通 Demo 完全可能。做生产级落地,你需要考量的就不只是业务 API 的难易程度了:
- 协同服务的部署需要自行解决。Univer 的前端再好,协同终归需要一个稳定的服务端。自建 WebSocket 服务集群要投入运维资源,必须有专人来盯。
- 自定义沉浸式功能时,你大概率需要深入到插件系统、渲染层、甚至是它的数据流内部。如果团队没有熟悉架构的人,启动成本会被拉高。
- 版本演进速度快是好处也是代价。0.x 阶段迭代到正式 1.0 之间,踩到 breaking changes 的概率相当高。
我个人的结论是:如果你们团队已经有成熟的前端基础设施和 Node 后端能力,可以承受一定迭代成本,那 Univer 的开源生态、架构先进性和功能完整度,放到同类项目里几乎找不到能打的替代品。如果团队只是需要展示型表格,那它可能过于重了——这是实话。
6. 个人经验与最后建议
从我第一次 inspect Univer 的源码,到现在已经在生产环境支撑了数个内部项目,我对它的评价没有变过:架构优秀,理念超前,技术路线清晰,但现阶段仍然需要"懂它的人"来驾驭。
如果让我给正在评估它的人提几条最直接的建议,那首先是:选好一个版本以后,立刻锁定,然后在代码仓库里对你的业务代码做版本打标。千万不能让别人随手升级。第二个建议是:动手做二次开发之前,先花一两个晚上把它的命令系统读通。这是你能发挥扩展能力的基础,不读通这个,你的所有自定义功能都容易走入死胡同。第三个建议是:协同能力能早点接入就早点接入。生产项目往往是后期才想加协同,那时候再改架构,比一开始就同步设计,代价高得多。
还有一个实际装机时的小心得,也顺手写在这里:如果你的业务有大量预置模板,可以考虑服务端运行无头模式批量预渲染模板成 JSON,前端拿到 JSON 后直接加载,这样首屏渲染速度会快很多。这个技巧我试过,效果很直接,表格从空白到完全可用的时间几乎减半。
Univer 还在快速生长。我期待着它的 1.0 稳定版本,也期待整个生态越来越丰富。如果你已经开始用它了,或者正准备上手,希望这篇文章能在你踩坑之前给你一些帮助。说到底,它真的是个值得认真对待的好项目。