资讯详情

Univer实战:从选型到落地的Web在线Excel方案

📅 2026/11/3 6:06:28 | 华诺云谱 👁 阅读
Univer实战:从选型到落地的Web在线Excel方案
前阵子接了一个数据中台的项目客户给的需求特别简单粗暴我们要做一个在线 Excel能填数、能复制粘贴、能导出来给别人最好看起来跟 Office 那套差不多。我去翻了一圈现成方案手写虚拟滚动网格不现实商业表格控件价格谈不下来最后把目光放到了 Univer 上。用了几个月把表格能力真正嵌进了业务系统这里面的接入过程、数据操作细节、性能边界和 Excel 兼容性坑我觉得值得好好写一篇给正在做同类选型的朋友参考。1. 一个在线 Excel的需求为什么最后落在 Univer 上先说结论Univer 是目前非常适合做 Web 端嵌入式表格的开源办公套件尤其当你需要的不是能看不能编辑的静态报表而是要支持真实录入、公式、样式、甚至后续协作能力的复杂网格。它是 Luckysheet 团队做的下一代项目一开始我也有疑问既然 Luckysheet 能用为什么要换实际对比之后发现两者的设计思路差了一代。1.1 从 Luckysheet 到 Univer同一个团队不同的答案老前端应该都接触过 Luckysheet它当时能实现在浏览器里面跑到接近 Excel 的交互确实惊艳。但它更像一个功能完整的单页表格应用想要嵌进自己的系统里做定制就要面对它相对固定的数据结构和插件边界。Univer 是同一批人基于前面的经验重新做的框架核心变化是把表格这个产品拆成核心引擎 渲染引擎 业务插件的架构。你拿到的不再是一个只能打开就用的表格应用而是一套可以按需组装的能力包需要表格就装表格插件需要公式就装公式插件不需要的东西完全可以不引入。网上现在搜univer在线多半指的是官方提供的那套在线演示环境里面有表格、文档、幻灯片等模块可以直接体验。这也能看出来它的目标已经不只是做一个 Web 版 Excel而是要做一个浏览器里的办公套件底座。从我实际接手的项目来说表格部分是最成熟的文档和幻灯片还在快速增长中如果你当前只需要表格完全不影响使用。1.2 Univer 到底解决什么问题围绕在线 Excel这个需求最容易遇到三个老大难渲染性能、Excel 文件兼容、后续扩展空间。性能上传统做法是用 DOM 表格渲染列数少、数据量在几百行的场景没问题但到了几万行、几十万行DOM 节点数量会让页面越来越钝。Univer 采用 Canvas 渲染视口内可见区域才真正绘制滚动时不断重绘这带来的是数量级上的流畅度提升代价则是选区、点击命中这些交互逻辑都得自己处理。好在框架把这些处理好了使用者只需要关注业务数据。Excel 兼容性上Univer 做了大量工作来对齐 Office 的文件格式和样式模型支持导入导出常见的 xlsx 文件也实现了相当规模的公式引擎。也就是说你在 Excel 里做好的模板导进来之后多数情况能保持样式、公式、合并单元格等基础结构。扩展空间上Univer 的插件机制让我整个项目组都觉得踏实。我们做得比较多的是把公司内部的校验规则包成一个插件表格本身升级版本不影响校验逻辑反过来自定义插件出了 bug 也不会拖垮核心表格。1.3 适合哪些产品和技术团队如果你属于下面这几类情况Univer 值得认真评估低代码平台要提供表单子表或数据录入组件 SaaS 产品需要在线预算表、排期表、运营数据填报工具企业内部系统要做 Excel 导入预览和在线修正后再入库或者你想省掉商业表格授权费用用开源方案做定制合入自己产品。反过来如果只是要做只读报表展示Univer 的接入成本相对偏高用更轻量的可视化表格组件更划算。团队里如果没有人能 hold 住前端依赖管理和 Canvas 渲染相关的排错也需要重新评估因为版本更新和 API 调整会占据一部分维护精力。2. 把 Univer 接进项目的完整过程Univer 的接入方式和大多数前端库类似但有几个细节一开始很容易踩到我在项目里也折腾了一两天才完全跑通。下面这段基于我当前项目锁定的版本Univer 迭代速度很快命名和入口可能会有调整动手前务必打开官方文档确认当下写法。2.1 安装与最小 Demo我在项目里用的是 npm 安装核心包包括univerjs/core作为基础核心univerjs/sheets提供表格能力univerjs/ui提供界面元素另外还需要univerjs/engine-render作为 Canvas 渲染引擎。根据你要的功能后面可能还要加公式引擎、导入导出插件。安装命令看起来是这样npm install univerjs/core univerjs/sheets univerjs/ui univerjs/engine-render初始化一个最简单的表格代码可以这样写import { LocaleType, Univer } from univerjs/core; import { UniverRenderEnginePlugin } from univerjs/engine-render; import { UniverSheetsPlugin } from univerjs/sheets; import { UniverUiPlugin } from univerjs/ui; const container document.getElementById(app); const univer new Univer({ locale: LocaleType.ZH_CN, container }); univer.registerPlugin(UniverRenderEnginePlugin); univer.registerPlugin(UniverSheetsPlugin); univer.registerPlugin(UniverUiPlugin); const workbook univer.createUniverSheet({ sheets: [ { name: Sheet1, rowCount: 100, columnCount: 20, cellData: { 0: { 0: { v: 项目名称 }, 1: { v: 负责人 } } } } ] });我把这个文件放进去容器设置了宽度和高度页面刷新就看到一张带工具栏的空白表基本 Demo 就跑通了。这里有一个经验容器高度必须显式设置否则 Canvas 初始化时拿到的高度是 0页面不会报错但你会看到一个诡异的空白。后面排查空白问题时我会再提。提示Univer 的插件注册顺序一般建议先注册渲染引擎再注册业务插件界面插件放最后。如果你注册顺序不对有些功能不会立即报错但会表现为工具栏缺失或者表格不绘制。遇到这类情况优先检查注册顺序。2.2 从创建实例到写入数据表格实例创建后你需要拿到工作表对象去操作数据。在我用的版本里数据写入和读取都通过 workbook 和工作表实例的方法进行。大致是这样const sheet workbook.getActiveSheet(); sheet.setCellValue(0, 0, 订单号); sheet.setCellValue(0, 1, 客户名称); sheet.setCellValue(0, 2, 金额);如果要同时设置值和样式通过单元格数据对象的方式更直接。样式用类似 CSS 的对象表达背景色、字体、边框等都可以配置sheet.setCellRaw(1, 0, { v: SO-2024001, s: { bg: { rgb: F2F2F2 }, bl: { s: 1 }, ht: { v: 12 } } });这里需要特别提醒Univer 内部的数据状态是统一管理的你不要直接去修改传入的 cellData 原始对象也不要绕开 API 去改内部状态。我刚开始图省事从接口拿到数据后直接往原始的cellData树里塞字段试图让表格跟着变结果发现视图没有任何反应。正确做法是通过 sheet 实例提供的操作方法或者通过命令机制触发更新这样框架才会重新计算并刷新画布。2.3 从本地 placeholder 到真实业务数据实际项目里你不会真的一格一格 set 值而是拿到后端数组后批量写入。我是这样处理的先根据数据的行列数调整工作表尺寸再遍历数据组装 cellData最后调用批量写入接口。批量写还有一个常见坑如果你同时设置大量单元格的值和样式建议先一次性构建完整的单元格数据对象再执行一次批量操作而不是循环里频繁调用接口。性能差异非常明显循环调用在大数据量下会白屏好几秒。Univer 对命令做了合并和优化你一次交给它的数据越完整渲染引擎重绘的次数越少。拿到后端数据的典型代码长这样const rows apiResponse.data.map(row { const rowCellData {}; row.forEach((value, colIndex) { rowCellData[colIndex] { v: value }; }); return rowCellData; }); sheet.setRangeValues(0, 0, rows);setRangeValues这类接口通常按行列范围批量生效。这里的底层逻辑是每次调用都会触发一次视图更新和可能需要重算公式所以把一批数据塞进一次调用比拆成很多次要合理得多。3. 常用的数据操作与样式细节表格嵌入业务系统后最常见的需求其实就是四类填值、改样式、写公式、监听变化。每一类用起来都有需要注意的地方我分开说。3.1 数据与状态的分层为什么不能直接改 cellDataUniver 内部对工作簿、工作表、单元格有一套状态模型UI 上看到的表格只是这个状态模型通过 Canvas 渲染出来的结果。所以数据流是单向的业务数据先更新状态模型模型通知渲染引擎重绘。理解了这一点你就能明白为什么直接改初始传入的cellData是无效的也明白了为什么所有官方示例都在用 API 操作。这种设计的价值在于命令可以记录、可以撤销重做、可以用于协同场景的变更同步。我们在做自己的业务时所有批量修改都希望通过表格的命令服务执行这样后面要做操作历史或者协同底子就已经打好了。3.2 设置值、公式、样式和合并单元格设置值和设置公式是两条线路。普通值直接给v字段公式则用公式字段等表达。我一开始把公式字符串直接丢进v表格把它当纯文本显示闹了个尴尬。正确方式是区分字段需要让公式引擎参与计算的内容要走公式字段这一层等到公式引擎计算结果返回后单元格才会显示最终值。公式写完之后不要急着马上读取结果因为计算是异步的。尤其是复杂公式或跨表引用立即 get 可能拿到旧值。我给前端同事的约定是写入公式后监听公式计算结果变化或者在用户主动操作后再读取显示值。合并单元格在业务里也比较常用比如表头要跨列居中。操作方法通常是在一个范围内执行合并命令。注意合并方向要符合 Excel 习惯合并后保留左上角单元格的值其余区域的值会被覆盖或隐藏所以合并前要把右上角、左下角这些位置上的值先挪走。样式方面我没有把整套配置背下来而是直接参考类型定义。它的样式结构和 Excel 内部模型很接近概念包括背景色、字体、边框、对齐、数字格式等。有一个技巧定义样式时尽量复用样式对象而不是每行内联一个全新的样式对象因为框架内部对样式做引用和缓存如果每个单元格都独立建立一份结构相同的样式内存占用会上升。3.3 事件监听与状态快照业务系统里用户改完表格数据前端要知道哪些格子变了、变成什么了这个能力直接决定你回写接口的实现复杂度。Univer 提供了一套事件机制监听范围包括选区变化、单元格变更、公式计算等。我在项目里做的是监听单元格变更事件把变更的行列坐标和值收集起来等用户点保存时统一提交给后端。这里有个细节事件回调里的单元格值可能还是输入前的旧值想要拿到新值必须在正确的事件阶段去读取。我没有花时间去看内部状态堆栈而是用了更稳的办法在保存按钮触发时直接遍历业务关心的整张表或者变更范围读取最新值。如果需要做撤销/重做按钮Univer 的命令栈已经天然支持了大部分操作你可以直接调用对应命令。我们项目里给用户暴露了撤销重做入口基本没花什么额外工作量这就是架构设计带来的红利。4. 大数据量与性能渲染机制和调优边界能打开一百万的 Excel是我当时给产品画的饼实际做下来发现打开和流畅使用是两回事。Univer 的渲染机制在 Web 表格里已经属于第一梯队但它解决不了所有数据场景问题你需要理解它的能力边界。4.1 Canvas 渲染和 DOM 表格的差异传统的表格组件用 DOM 生成行列一个 20 列 x 5000 行的表格就是十万个单元格节点哪怕每个节点只占一个空 div浏览器也会被拖垮。Univer 选择 Canvas同一时刻只绘制视口范围内的单元格滚动时重新绘制可见区域这让它的空表格渲染成本极低滚动性能也很接近本机 Excel 的感觉。代价是 Canvas 里的文字、单元格状态都是画上去的不是真实 DOM这意味着你不能像以前那样给某个单元格挂一个 HTML 弹窗或者自定义输入控件。Univer 通过对编辑器、下拉框等做了特殊处理来解决交互但你在做自定义需求时必须有这个意识高度定制化的控件要站在框架提供的机制之上而不是蛮力去操作 Canvas 像素。4.2 在百行、十万行、百万行下实测我简单做了一个压测在普通办公笔记本上用 Univer 打开一万行数据几乎没有感知打开十万行数据的明显瓶颈在数据组装而不是渲染批量写入之后滚动依然顺滑。到百万行数据时滚动没有死机但能感觉到帧率下降而且一旦触发大范围的样式重绘或公式计算会有明显的等待时间。这里很关键的一点百万行的压力主要不在渲染而在数据处理链路。如果这百万行只是放在那里对 Canvas 渲染来说就是大量的惰性区域滚动依然能撑住但如果你把一百万行的每一格都套用不同样式对象或者写了一个跨大量区域的数组公式那负担会全部压在内存和计算上。4.3 性能调优的四个优先级根据我的实测经验如果你要在大数据量下保持流畅按下面的顺序做优化别把大数据全灌进前端。最有效的方案是服务端分页、按需加载、范围查询。用户真正需要看的永远是一个视口和数据子集。批量更新避免频繁小操作。所有数据更新都合并成一次命令减少状态模型和渲染引擎之间的往返。样式保持统一和复用。能用默认样式就不单独配置每格。那些看起来一样但每个格子都有独立内联样式的表格是内存杀手。关闭不必要的交互反馈和高级功能。如果业务里不需要右键菜单、动态边框高亮这些效果能不引出插件就不引出公式引擎只在明确需要公式的工作簿里启动。我在项目里最终采用了双层方案用户导入的 Excel后台先解析为标准 JSON前端加载时只取当前视图范围内的数据行和必要的汇总字段用户想看更多滚动到接近底部时触发下一页加载。这样既保留了 Excel 式的编辑体验又不会被撑爆。5. 与 Excel 的兼容性哪些通吃哪些要兜底大多数需求都绕不开一个前提用户手上的数据在 Excel 里做完的表也要导回 Excel。Univer 对 Office 兼容做了很多努力但兼容不等于100% 无损我在项目里专门处理过几类不愉快。5.1 导入导出与相关软件互通Univer 提供了导入导出插件支持常见的 xlsx 格式和 CSV。我测试的常规数据场景数值、文本、基础字体、背景色、边框、合并单元格、简单公式这些在 Excel 来往问题不大。尤其是表格模板如果本来就是从 Excel 里做出来再导入的样式还原度会比较高。导出到 Excel 后我用 WPS、MS Office、在线办公服务几个环境分别打开验证发现主流环境的表现基本一致。如果只是日期的格式略有偏差通常是 Excel 日期序列号和前端日期对象互转时的问题需要在导出前把日期字段统一成字符串或者设置数字格式。5.2 条件格式、数据验证、图表等高级特性的现状复杂功能要谨慎。条件格式在很多开源表格里都只是能导入不能编辑的程度Univer 对数据验证的支持在逐步完善但如果你模板里用了复杂的数据验证公式、跨 Sheet 的条件格式或者以命名区域引用的数据验证列表导入后可能需要重新检查。图表能力也是逐步补齐的过程动态图表和 Excel 原生的图表对象之间仍然存在映射不确定性。我的处理策略是提前给业务方打好预防针基础数据编辑场景完全可用但带有大量条件格式、切片器、透视表、宏的 Excel 文件不适合直接丢进前端编辑。这类文件应该保留在 Excel/桌面工具里处理导入到 Univer 的主要是干净的数据模板。5.3 我踩过的格式转换坑第一次把客户模板导入系统时出现了两个典型问题。一个是数字变文本Excel 里的数字列可能被识别成字符串因为原始表格里该列有部分单元格确实是文本格式。这个问题不能完全靠导入插件解决我选择在后端做类型嗅探读取前 100 行如果绝大多数是数字就把文本单元格转成数字再下发前端。第二个是工作表名称和自定义视图的干扰。某些 Excel 工作簿里含有隐藏工作表、宏表或外部链接导入时会导致某些单元格报错或显示为空。我的兜底做法是用户上传后在服务端做格式清洗移除多余的工作表、清空外部链接只保留有用的数据区再给前端渲染。这不完全是 Univer 的问题但你在做实际系统时必须要提前准备这一层。6. 常见问题的排查清单和团队协作接入期和开发期遇到的坑我整理成一份排查清单给团队当 wiki 用。问题集中在几处照着顺序查通常能很快定位。6.1 渲染空白、公式不计算、样式丢失排查表格区域渲染空白十有八九是容器尺寸问题。要么是父元素高度塌陷要么是页面初始化时容器处于隐藏状态。我曾经把一个表格放在 Tab 页里Tab 默认不激活切过去之后 Canvas 不刷新结果一片白。解决方法是切换到可见 Tab 后手动触发一次尺寸刷新。公式不计算分两种一是我前面说的写入方式不对公式写进了值字段二是公式引擎插件没有注册。Univer 里公式能力是独立插件如果你只注册了表格插件而没有注册公式引擎页面不会崩溃但所有公式都会显示原文本。如果你自定义了插件还要检查公式引擎的初始化顺序它依赖渲染引擎已经就绪。样式丢失大概率是结构不对。Univer 的样式对象调整过我之前把旧版示例里的样式结构直接搬过来背景色没有生效。遇到样式不生效不要靠猜打开官方在线示例把它的样式对象打印出来对比字段。6.2 多人协作与后端数据回写Univer 支持协同编辑但它是能力而非开箱即用产品。你需要自己设计协同服务端、WebSocket 通道、变更合并策略再通过 Univer 的协作接口接入。我的建议是分三步走先做单用户编辑后端保存保证数据回写正确再做冲突检测防止两人同时改同一张表导致互相覆盖最后才做真正的多人实时协同。一步到位的协同方案会把你拖进无穷的调试黑洞。后端回写这一块前端只是把变更范围发过去真正落库前后端必须做二次校验。我踩过的坑是用户在表格里复制了一列含有换行符的内容前端拿到数据的表现是正常的但 JSON 序列化后换行符被转义后端没有正确解析导致入库数据错位。后来我在前后端都统一了文本清洗逻辑专门处理换行、制表符这类隐藏字符。6.3 我对插件化和版本迁移的一点建议Univer 最有价值的地方就是插件化但它也意味着你要接受版本迁移的负担。我们内部有一个原则把业务逻辑包在自定义插件里不在项目代码里到处直接调 Univer 的底层 API自定义插件和核心包的接口通过一个薄薄的门面层隔离。这样每次 Univer 升级只需要改门面层的适配代码而不用全局搜索替换。版本锁定时不要直接依赖 latest。我在早期偷偷尝鲜一个 alpha 版本结果构建报错回退到稳定版之后发现包的 peerDependencies 不兼容折腾了很久才把依赖树理顺。现在我们的做法是固定小版本号升级时单独跑一次全量回归重点验证公式、样式、导入导出这几个高影响模块。我个人在实际操作中的体会是Univer 不是拿来即用的业务产品而是一个高质量的开发框架。如果你的团队愿意投入两三周时间吃透它的核心概念和插件机制后面能收获的长期价值远超过在商业控件上省下来的那笔授权费。最后再分享一个小技巧接入初期遇到任何表现诡异的问题第一反应打开官方在线示例对照如果官方示例没问题那就是我们的写法问题如果官网上能复现那就是版本问题。这招能帮你省掉大量在源码里打日志的时间。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑