ARTICLE · 1041924
我们做了个 Cocos 配表插件:让错误出现在打包之前,而不是上线之后
一、先说那个最容易被忽略的炸点
如果你的项目里有一张表,某个字段存的是资源路径——db://assets/.../icon_new.png 这种——那你大概经历过(或者即将经历)这么一幕:
包打出来了,功能都通了,测试第二天早上说"新手礼包那个图标是空的"。

回头看,路径错了一个字母,或者那张图根本没有人导入过项目。表本身没写错格式,代码也没写错逻辑,只是从策划填表到进引擎调用,中间没有任何一个环节,在错误发生之前看过它一眼。
配表这件事,说起来特别简单:策划填数据,程序读数据。但真到项目里,它是一条完整的链路——
定义结构 → 填/导数据 → 产出可运行的类型与数据
大多数配表工具只做了中间那一步的一半:把你的 Excel 读成一份 JSON。至于结构是"猜"出来的还是"定义"出来的、数据里的资源路径真不真、键名和外键对不对得上、这份数据进到 TypeScript 工程里会不会把编译和包体一起拖垮——它不管,因为那"是你的事"。
结构从哪来:模板驱动
大多数项目要配的表,翻来覆去就那么几类。所以插件内置了7 套标准模板:道具、关卡、任务、商城、Buff、多语言、自定义。选一个模板,字段就给你铺好了(id、名字、图标、价格……),可以直接用,也可以在它上面改;想从零设计,就选"自定义"或直接新建空表。

模板里跟着的不只是字段,还有默认的校验规则和引用关系。这是"模板驱动"和"扔给你一张空表"的区别:新表一建立,规矩就已经在那儿了,而不是等出了错再回头补。
YYKATableConfig 是我们为这条链路做的一个 Cocos Creator 插件(当前版本 3.1.6,支持 Cocos Creator 3.8.0 及以上)。下面不讲情怀,讲我们具体拦了哪些坑、用的什么办法,以及它现在确实做不到什么。

二、错误要在"进引擎之前"被拦下来
全部能力里,我们投入最多的是真实资源校验,也是我们最想让你先看的一条。
原理不复杂:你在表里填的图片、音效、Prefab 路径,我们不把它当成一个字符串,而是拿去和当前 Cocos 工程真实的资源库比对一次。找不到就是找不到,当场标红。
举个我们自己做演示时用的例子:把某一行的一个资源字段填成 db://assets/img/no_such.png,点一下「校验」,这一行立刻被标成红色,提示资源不存在。
【图 1:把伪造的资源路径填进表里 → 点校验 → 该行标红"资源不存在"】
就这一下,"新手礼包图标是空的"这种事故,就从"测试第二天早上发现"提前到了"填表当天下午发现"。对你来说,省掉的是排查时间;对项目来说,省掉的是一次线上事故。

校验不止查资源,还有一整套规则
配一个字段的时候,你可以顺手把规则定下来:非空、唯一、数值范围、数组格式、枚举、正则。这不是装饰,是你在数据进来之前立下的规矩:
主键撞了?唯一性校验当场报出来,不用等运行时两边数据互相覆盖; 一个"品质"字段只能是"白绿蓝紫橙"?枚举写错一个再也不用靠人眼对; 数值范围写反了(比如"价格区间 min > max")?规则里定好,配错就拦。
还有跨表引用
这一条是我们认为最能区分"配表工具"和"读 Excel 的脚本"的地方。
比如商城表里有一个 itemId,它必须是道具表里真实存在的 id。你可以在字段上声明它引用哪张表的哪个键(ref: { table: 'item' }),然后点「跨表校验」:如果商城表里写了一个道具表里根本不存在的 id,同样当场标红。
【图 2:跨表引用校验,指向不存在的 itemId 报红】
一张表内部的错误,工具能查;表与表之间的错误,才是最容易被漏掉、也最贵的那一类。
说回那个区别
如果你习惯了自己写脚本读 Excel,可以对着这几条看一眼,判断这个插件对你有没有价值:
ref 后跨表校验 | ||
.ts,表大了拖编译和包体 | ||
三、数据不进 TS:大表不该拖垮你的编译和包体
这是我们花心思最多、也是同类工具最少做对的一处,值得单独讲。
最省事的做法,是把整张表的数据直接生成为 .ts 文件,程序 import 进来就能用,类型还是现成的。表小的时候非常爽。但表一多就出问题:
几千行数据变成一个巨大的 TS 模块,改一行数据要重新编译整个模块; 数据以 TS 代码的形式存在,编译和包体要同时承担这一份体积; 数据一多,编辑器打开这个大文件就卡。
我们的做法是:数据留在 JSON,TS 侧只生成"类型 + 按表加载函数",分表懒加载。你要哪张表就 await 哪张,不用的不加载。

真实的产物用法是这样的:
import { YKTableData } from './yyka_table_config/ykTableRuntime';import { loadItem, getItem } from './yyka_table_config/item';// 入口处注册一次数据读取实现(Cocos 里用 resources.load 读 yyka_table_config 目录)YKTableData.setReader((relPath) => resources.load(`yyka_table_config/${relPath}`, ...));const itemRows = await loadItem(); // Promise>const sword = await getItem(1); // Promise
一次导出产出的其实是三个文件:
item.json | assets/resources/yyka_table_config/ | |
item.ts | loadItem 加载函数 | assets/scripts/yyka_table_config/ |
ykTableRuntime.ts | YKTableData) |
导出成功后自动刷新编辑器资源库,不用手动切前后台。
这里也要把话说清楚,免得你有错误期待:TS 类型拦不住 JSON 里的内容写错——比如资源路径不存在,那是校验环节的事,不是编译期的事。类型解决的是另一半问题:你在代码里写 item.naem 时编辑器会直接画红线,字段名和枚举不用再靠记和猜,也不用反复"复制 → 粘进代码 → 对着键名翻来翻去"。
四、导入既有的表:不允许"稀里糊涂覆盖"
现实里没人从零开始配表,大家都是先有一堆 Excel 和 CSV。所以我们把导入做得比较谨慎:
表头按字段名或中文别名匹配——你写 id、写ID、写"编号",只要 schema 里配了别名都能对上;#和 //开头的注释行列直接跳过,不进数据;匹配不上的列会被忽略,预览确认之后才写入,不直接落库。
拖拽导入我们也做了:把 Excel、CSV、JSON 拖到面板主体区,会先弹一个嗅探确认弹窗,告诉你识别到了什么、准备怎么写,你确认了才落库——因为你辛苦配过的表,不该被一次误拖干掉。
【图 3:拖拽导入的嗅探确认弹窗】
导出侧同样留了后路:json / ts / csv / excel / bin多格式,支持增量导出(只改了几行就只导这几行)和导出前自动备份;备份可在面板里查看和恢复,误操作不至于把工作区弄丢。
五、一份核心,三种入口
配表这件事,人和机器都得能驱动。我们把导出做成一件事的三种入口,走同一份核心:
① 编辑器里点。配表页增删改查、字段设计器(可视化加字段、拖拽排序、配引用表和引用键)、导入弹窗、日志停靠条,日常配表就在面板里完成。
② 命令行跑。仓库里带一个独立 CLI,不需要打开编辑器:
# 工作区所有表导出 json + tsnode bin/yyka-export.mjs --workspace examples/item_ws --out ./dist --formats json,ts# 只导 item,开增量与备份node bin/yyka-export.mjs --workspace ./ws --tables item --formats json,bin --incremental --backup
有错就返回非 0 退出码,可以直接卡住 CI 流水线(示例 workflow 在仓库里)。这条通路是做工具的人必须给的:你不可能要求 CI 环境里跑一个编辑器。

③ AI 通过 MCP 调。插件内置 MCP 服务,把配表能力开放成工具:建表、按模板生成数据、行的增删改、单表校验、跨表校验、导出、模板列表、操作日志查询。支持 stdio 和 http 两种传输,面板里有启停开关和收发日志,方便你排查是模型写错了还是工具没接上。
另外一条是"自动":打包联动。在 Cocos 里打包之前,插件会自动触发一次导出前校验。这是我最想要的一步——让配表的错误出现在打包之前,而不是上线之后。
六、它现在做不到什么
不吹的部分也写下来,省得你装了之后觉得被骗:
它只管"配表链路",不碰引擎行为。 不做场景读写、不碰预制体实例化、不做资源导入管线。那些是引擎和编辑器的事,我们不假装能做。 资源校验是"存在性校验"。 它能告诉你这个路径在资源库里找不到,但它不能替你判断"这张图标的美术风格对不对"。 MCP 生成数据有行数上限(单次 200 行,并且会返回全表)。这是为了不让一次调用把你的工程写爆;要大批量,走 CLI + 增量导出更稳。 没有云端。 工作区就放在你的工程目录里,团队协作靠 Git 传文件。表数据不出你的机器——这是我们一开始就定下的边界,不打算改。
配表这件事,看着不起眼,但一个项目能不能稳稳推到上线,往往就取决于这些"看似琐碎、实则致命"的细节有没有人替你兜底。
我们做这个插件,想说的其实只有一句:让"配表"这件事,在进引擎之前就是对的。