
先把结论放在前面如果你正在找一款能在网页里在线编辑表格、支持业务方自己定义填写区域、还能把其余单元格锁死不允许乱改的开源表格引擎Univer 非常值得放进你的考察名单。我最早关注 Univer是因为团队内部一个反复踩坑的需求业务同事每周要收集一批数据之前一直是把 Excel 模板发到群里大家下载、填写、再回传。一次两次还好人一多就乱有人改了不该改的公式列有人用 WPS 打开后格式错乱最后汇总还得人工对拍。后来我们决定做一个内部数据收集系统核心要求就三条模板由业务方自己定义参与者只能在指定单元格填写其余区域不能编辑。市面上的表格组件看了一圈最后落在 Univer 上就是因为它在“在线协作、单元格权限、Excel 兼容”这三块刚好都能覆盖而且完全开源、可以私有化部署。这篇文章我会从 Univer 的项目定位、权限模型、实际开发接入、常见坑这几个角度讲清楚尤其重点拆解“开放一部分单元格、锁死其他区域”这个典型场景。如果你正在评估在线表格技术方案或者马上要动手写一个可填充可锁定的报表系统这篇应该能帮你省掉不少调研时间。1. Univer 到底是什么跟其他表格库差在哪1.1 一个能在浏览器里跑的“Excel 引擎”Univer 是一个基于 TypeScript 开发的开源办公套件目前最成熟、最常用的是它的 Spreadsheet电子表格能力同时也在向文档、幻灯片方向扩展。它不是一个“表格导出工具”也不是只管渲染的静态组件而是一整套从数据模型、公式引擎、渲染层、交互层到协作层的完整表格解决方案。你可以把它理解成“把一个迷你 Excel 塞进浏览器再用 API 控制它的一举一动”。它最大的特点是不依赖服务端渲染纯前端运行数据可以直接给到远程接口也可以本地导出文件。这意味着它能像普通前端组件一样被嵌入到 React、Vue 项目中同时又具备完整表格软件的操作体验单元格编辑、拖拽填充、条件格式、数据校验、公式计算、筛选排序都有。值得注意的是Univer 也支持后端协同能力多人同时编辑一张表时能通过协同算法合并操作这点在同类开源表格库里并不多见。1.2 为什么大家都在选它几条硬指标单说“开源在线表格”国内开发者以前接触比较多的是 Luckysheet、x-spreadsheet 那一类项目。Univer 之所以在社区里讨论度快速上升核心是这几项能力凑在了一起一是插件化架构。Univer 核心包本身只负责基础框架和数据模型公式、权限、协同、导入导出全部以插件形式挂载。需要什么功能就装什么插件不需要的插件不引入最终打包体积可以控制得很低。二是Excel 兼容性。Univer 支持导入导出 xlsx 文件格式还原度在开源项目里算是做得比较细致的。内部系统如果需要和外部交换报表这一条几乎是刚需。三是协同能力。它支持类似 Google Sheets 的多人协作模式而且是把协同作为基础能力设计的不是后加的补丁。四是权限可编程。单元格、行、列、工作表都可以设置锁定与保护状态并且支持通过代码动态控制。这就是我自己项目里用到的最核心能力后面会重点展开。拿它和几个常见方案对比一下大概长这样能力维度UniverLuckysheetHandsontable开源协议开源可商用开源社区版有一定限制非商业需要授权单元格权限锁定支持且可编程支持较弱支持偏商业版协同编辑内置方案可接入依赖二次开发需要企业版Excel 导入导出xlsx 原生支持支持细节有损耗支持高级功能付费前端框架适配React/Vue/原生均可用依赖 jQuery 生态框架适配较好从表格能看出来如果只是做一个展示级表格很多库都能胜任但如果要“在线协作 单元格权限 业务自定义填写”Univer 几乎是唯一一个开源协议友好、能力路径完整的选择。2. 核心场景拆解开放填写单元格锁死其他区域2.1 先把需求讲清楚哪些单元格可以动哪些不行很多初次接触 Univer 的开发者会混淆“单元格锁定locked”和“工作表保护protect”这两个概念。这里有必要先厘清Univer 的权限逻辑继承自 Excel 的经典模型单元格本身有“锁定”状态但这个状态默认不生效只有在整张工作表开启“保护”后被标记为锁定的单元格才会真正不可编辑。反过来如果某个单元格既处于保护工作表下又被主动设置为“未锁定”那它仍然可以填写。这正好对应我们前面那个需求模型整张表保护起来默认所有单元格都不可编辑再把需要参与者填写的单元格显式解锁。这样做的好处是新插入的单元格默认也被保护住不会出现“漏了一个地方没锁”的情况。哪怕业务同事后来往表格里加了一列那新增单元格也天然处于锁定状态必须显式开放才能填写。这种模型在 Excel 里叫“保护工作表 取消锁定区域”在 Univer 里本质上是同一套思路只是把配置从桌面端操作搬到了代码里可以动态下发到不同用户或不同表单。2.2 “用户自定义表格”到底是怎么落地的这个场景里的“用户定义表格”核心含义是业务同事不写代码也能自己定义一张可填写的在线表格结构。具体在 Univer 里拆开就是几件事的组合第一定义表头与样式。业务方可以自己在界面上调整列名、列宽、背景色、边框这些操作会实时反映到表格数据模型中。第二设置区域填写规则。哪些列是录入项、哪些列是公式列或合计列业务方通过锁定/解锁操作就能表达。第三让参与者看到一张“干干净净”的表。别人打开表格时只能看到模板结构只能编辑被允许的区域公式和底表数据看不到或者改不了。第四数据统一回收。参与者提交后表格数据可以直接上报到服务端不再需要人工合并回传文件。这套东西如果从头自研光是一个类 Excel 的界面渲染和单元格交互就要写很久更别说公式计算、样式还原这些细节。用 Univer 等于把最重的底层都租来了团队只需要聚焦在“业务规则”和“数据流”上。2.3 解锁和锁定的两种常用配置方式Univer 做单元格开放填写实际操作上一般分两种路径。一种是在界面上操作。表格开启保护后选中要开放的单元格区域右键或通过命令面板执行“取消锁定”操作。这种方式适合业务同事在模板定义阶段自己操作不需要开发介入。另一种是纯代码方式。初始化工作表时通过样式循环或 API 直接标记单元格锁定状态。这种方式适合模板由后台生成、或者需要在运行时按用户动态计算的场景。两种方式也可以混用业务方先在线调整好模板开发再把模板配置持久化到后端用户打开时Univer 加载模板配置权限规则直接生效。这样既保留了业务方的灵活性也保证了每一次打开都是一致的状态。2.4 配合自定义工具栏能让体验更贴近业务我实际开发中的一个体会是Univer 默认的右键菜单和工具栏对普通业务用户来说有点“过于专业”但没关系它提供了命令机制和 UI 插件扩展接口。你可以把不需要的菜单项去掉只保留一个“填写说明”的浮动提示甚至可以在顶部加一个自己的按钮点击后校验必填项、然后提交数据。这样做的好处是用户看到的不是一个“开发工具”而是一个像问卷一样简单的填写表单。表格只是承载数据的壳业务体验完全掌握在自己手里。很多人低估了这一点总觉得“列表格就完事了”实际上把界面上多余能力藏好数据质量会明显上升。3. 实操从零跑通一个带单元格保护的 Univer 表格3.1 环境准备与依赖引入我以最常见的 React Vite 项目为例Univer 官方提供了 React 封装组件接入会比较顺手。非 React 项目也不用担心它本身是框架无关的原生 JS、Vue 都可以使用只是写法上略有差异。初始化项目后安装基础依赖npm install univerjs/core univerjs/ui univerjs/sheets univerjs/sheets-ui univerjs/sheets-formula univerjs/sheets-import-xlsx univerjs/sheets-export-xlsx这里每个包用途不一样core 是核心数据模型ui 是框架界面sheets 是表格引擎主体sheets-ui 提供工具栏和交互sheets-formula 处理公式计算import/export 负责 xlsx 进出。如果不需要公式最后一个公式包可以不加体积会小一块。安装完依赖之后还需要保证项目支持 CSS 变量和现代浏览器特性一般 Vite 默认配置直接够用。3.2 初始化 Univer 并挂载表格实例Univer 的初始化思路是“先注册插件再创建实例”。直接看代码import { Univer } from univerjs/core; import { UniverUIPlugin } from univerjs/ui; import { UniverSheetsPlugin } from univerjs/sheets; import { UniverSheetsUIPlugin } from univerjs/sheets-ui; import { UniverFormulaEnginePlugin } from univerjs/sheets-formula; const univer new Univer(); univer.registerPlugin(UniverSheetsPlugin); univer.registerPlugin(UniverFormulaEnginePlugin); univer.registerPlugin(UniverUIPlugin, { container: app, }); univer.registerPlugin(UniverSheetsUIPlugin); // 创建一张工作簿并添加工作表 const workbook univer.createUniverSheet(); const worksheet workbook.getActiveSheet();这段代码执行后页面上就会渲染出一个完整的在线表格。需要注意容器元素要有明确高度否则表格虽然初始化成功UI 区域可能被压缩成一条线。这个问题我第一次跑的时候也踩过后来直接给容器设置了height: 100vh才正常显示。3.3 设置工作表保护并开放填写区域接下来是重头戏默认锁死整张表然后开放指定单元格区域。Univer 中启保护、设置锁定状态有一套操作命令我先用“锁定全部单元格再解锁指定区域”的方式import { CommandType, ICommand } from univerjs/core; // 1. 设置整张工作表为保护状态 worksheet.setProtection(true); // 2. 将需要填写的区域例如 A2:C10设置为未锁定 const range worksheet.getRange(A2:C10); range.setStyle({ locked: { lock: false }, });这里的关键点在于保护是工作表级别的开关锁定是单元格级别的属性。设置setProtection(true)之后没有显式解锁的单元格全部变成只读A2:C10被单独标记成未锁定参与者只可以在这个范围内填写。还有一个细节是样式设置里的locked对象它包含一个lock布尔字段。有些开发者在接管 Univer 后会把注释填反导致设置后行为完全反过来。建议每次配置保护之后主动打开页面试一下能不能编辑。如果你希望动态控制某个具体单元格也很容易worksheet.getRange(1, 2, 1, 2).setStyle({ locked: { lock: false }, });参数含义分别是起始行、起始列、结束行、结束列行列从 0 开始计数。这种方式适合后台根据不同用户身份下发不同的可编辑区域。3.4 数据回收和导出填完的表怎么收回来单元格能填只是一个环节更重要的是把填写结果收回来。Univer 支持监听单元格变更事件典型的做法是把变更数据实时推给后端import { UniverInstanceService } from univerjs/core; univer.onCommandExecuted((command) { if (command.type CommandType.OPERATION command.id sheet.operation.set-range-value) { // 拿到变更的区域和值同步给后端 const params command.params; console.log(用户改动了, params); } });监听命令流是通用姿势因为用户既可能手工填写也可能通过复制粘贴、拖拽填充等方式修改单元格这些最终都会落到命令层统一拦截不会漏。如果提交方式偏向“整表提交”也可以走导出路径把当前表格导出为 xlsximport { UniverExportXlsxPlugin } from univerjs/sheets-export-xlsx; const exportPlugin univer.getPlugin(UniverExportXlsxPlugin); exportPlugin.exportXlsx();两种方式看业务需要。内部系统建议用监听命令实时上报这样后台可以做必填校验、冲突检测甚至实时汇总如果只是定期归档导出文件就够用。4. 常见问题与排查技巧实录4.1 设置了锁定单元格别人还是能改为什么这是我被问得最多的问题。大多数情况不是因为代码写错而是“保护”没开。可能你只做了setStyle({ locked: { lock: false } })但忽略了worksheet.setProtection(true)也可能反过来保护开了但锁定状态设置成了反逻辑。还有一个隐蔽点在 Univer 中如果工作表处于保护状态默认情况下连选择单元格的视觉反馈都会受限如果发现点击单元格没有选中框一般就是保护生效但解锁区域没配置成功。排查顺序建议是先确认保护开关是否为 true再确认目标单元格的 locked 是否明确设为 false最后看是不是右键菜单被自定义 UI 插件覆盖了导致界面用了另一套权限配置。4.2 多人同时编辑会不会有人绕过锁定这里要区分一件事Univer 的锁定是前端交互层的限制并不是后端安全边界。也就是说它保证了普通用户用鼠标点不进去但不保证有人通过构造命令消息直接修改单元格数据。如果是内部系统、用户可信度高这层限制足够如果对数据安全要求极严格正确姿势是后端也要有同样的权限校验规则前端锁定只负责体验后端权限才负责安全。我自己的项目里前端 Univer 锁好填写范围后端在接收变更时再校验一次“该用户是否有权改这个单元格”双保险。虽然前期多写一点代码但后面出现权限纠纷时能省很多麻烦。4.3 数据校验和单元格锁定的配合顺序很多人在做日报填写场景时既希望某些列不可编辑又希望填写列校验格式比如手机号必须是 11 位数字。Univer 中数据校验和锁定是两个独立维度互不影响。数据校验需要单独注册规则import { UniverSheetsDataValidationPlugin } from univerjs/sheets-data-validation; univer.registerPlugin(UniverSheetsDataValidationPlugin); // 给 A2:C10 添加“必填”或“数字”校验 worksheet.getRange(A2:C10).setDataValidation({ type: number, allowBlank: false, operator: between, formula1: 0, formula2: 999999, });配置数据校验后填写区域依然是可编辑的校验只对输入内容做约束。如果先后顺序搞反比如先删了数据校验再取消锁定表格会短暂出现空白校验状态但不影响权限本身。建议在模板初始化时先注册校验规则再设置锁定状态。4.4 大表格内存与性能问题Univer 毕竟是在浏览器里跑完整表格引擎一方面功能强另一方面也有性能成本。如果一张工作表有几万行数据并且同时开启了多个插件初次渲染和每次变更时的性能都会受到挑战。实测下来几个优化手段比较有效一是去掉不用的插件特别是公式引擎如果表内没有公式不注册公式插件能省掉明显的内存占用二是关闭不必要的 UI 功能比如剪贴板、查找替换等不常用工具栏能力三是大量写入数据时使用批量更新接口不要逐格setStyle一次性构建数据模型再渲染。Univer 也提供类似虚拟渲染的机制大数据量下表现会好很多但你要设计数据模型时尽量别再串行执行大量细粒度命令。实操建议汇总这个项目我持续用了几个月最大的感受是Univer 的价值不是“做一个表格页面”而是把表格变成业务系统的一个可交互数据组件。单元格锁定开放填写的模型并不复杂但它天然适合“模板由业务方定义、数据由参与者填写、结果被后台回收”的典型办公场景。如果你现在还在用“发 Excel 文件、群里等回传”的流程我建议直接把 Univer 嵌进内部系统里试试。先只做一张简单的日报表把官方 demo 跑通再把填写区域锁定最后接上后端命令监听整个链路很快就能完整走起来。等业务方尝到在线填写、实时汇总的甜头后续扩展权限分角色、自动校验、跨表引用都会顺理成章。