vue-vben-admin 表单项目如何执行 Zod 4 与 TanStack Form 迁移
【免费下载链接】vue-vben-adminA modern vue admin panel built with Vue3, Shadcn UI, Vite, TypeScript, and Monorepo. It's fast!项目地址: https://gitcode.com/GitHub_Trending/vu/vue-vben-admin
如果你维护着基于 vue-vben-admin 的表单项目,本次要完成的任务是:把表单校验 schema 从 Zod 3 升级到 Zod 4,并把内部表单引擎从 vee-validate 替换为 TanStack Form。迁移后业务侧的 Vben 表单 API 保持稳定,但业务代码中仍可能残留 Zod 3 写法与旧引擎 API,需要一次可核对的迁移。当前仓库的依赖已经处于迁移后状态,pnpm-workspace.yaml的 catalog 中声明了zod: ^4.4.3、zod-defaults: ^0.2.3和@tanstack/vue-form: ^1.33.2,全文档范围搜索不到vee-validate依赖,因此实际要做的是把存量表单代码迁到这套新依赖上,并按验收标准逐层验证。完整依据见 迁移指南。
迁移范围与依赖变化
| 类型 | 迁移前 | 迁移后 |
|---|---|---|
| Schema | zod@^3.25.76 | zod@^4.4.3 |
| 默认值 | zod-defaults@0.1.3 | zod-defaults@^0.2.3 |
| 表单引擎 | vee-validate@^4.15.1 | @tanstack/vue-form@^1.33.2 |
| Zod 适配器 | @vee-validate/zod@^4.15.1 | 不再需要,TanStack Form 支持 Standard Schema |
迁移完成后,源码、package manifest 和锁文件中都不应再依赖vee-validate或@vee-validate/zod。核心表单包 form-ui 的依赖即为迁移后形态:依赖@tanstack/vue-form、zod、zod-defaults,没有 vee 系列。
业务侧兼容范围:useVbenForm(options)仍返回[Form, formApi];FormApi的值、校验、提交、重置、schema 更新能力保留;FormSchema的fieldName、component、componentProps、rules、dependencies、defaultValue、已弃用的valueFormat和数组字段结构不变。formApi.form现在是库无关的FormContextApi,不再暴露 veeFormContext或原始 TanStack 实例。
前置条件
- 在一个干净的 Git 工作树中执行迁移工具,文档明确要求这一点。原因是下一条命令会原地改写文件,脏工作树会让
git diff无法区分迁移改动与未提交内容。 - 仓库 根 package.json 的
engines要求 Node^22.18.0 || ^24.12.0、pnpm>=11.0.0,后续验证脚本都依赖该环境。
第一步:运行 codemod 工具
按项目 tsconfig 执行固定版本的工具,把path/to/tsconfig.json替换为你要处理的实际 tsconfig 文件路径:
npx --yes zod-v3-to-v4@1.21.3 path/to/tsconfig.json执行前必须知道两个事实:
- 该工具会原地修改
.ts、.tsx和.vue文件,没有 dry-run 模式,这也是为什么要求干净工作树。 - 工具只能可靠识别直接从
zod导入的调用。通过@vben/common-ui或应用 adapter 间接取得z的 schema 需要人工审计,尤其是构造器错误参数、字符串格式和动态 refine 参数。
执行后立即检查git diff,确认工具改了什么、漏了什么,再进入人工修复。
第二步:人工修复工具覆盖不到的代码
Zod 4 的写法变化
构造器中的required_error和invalid_type_error合并为error,按输入动态生成消息时使用error(issue):
const count = z.number({ error: (issue) => issue.input === undefined ? 'Count is required' : 'Count must be a number', });字符串格式优先使用顶层格式 API,旧的z.string().email()等形式不应继续新增:
z.email('Invalid email'); z.url('Invalid URL'); z.uuid('Invalid UUID');错误列表改用issues,不要读取已移除的.errors:
const result = schema.safeParse(value); if (!result.success) { console.log(result.error.issues); }默认值方面,Zod 4 的 default 在输入为undefined时可以直接返回默认值;.default().optional()的结果必须按实际 parse 语义复核,而不是通过类型名称猜测。Vben 表单生成初值的优先级是:schema 显式defaultValue→ Zod schema 的.default()→zod-defaults生成的对象、intersection 和基础空值 → Vben 组件约定的空字符串、空数组或空状态值。必填标记以 schema 是否接受undefined为准。
另外几类需要逐处复核的写法:
- 不要读取
_def、_zod.def或typeName;公共包装器使用.unwrap() - TanStack Form 用 Standard Schema 校验时不会自动把 transform/coerce 输出写回表单 state,提交 payload 需要转换时使用表单级 codec;必须提交 schema transform 后的结果时,在 codec 的
encode边界显式调用parseAsync z.record()需要明确 key schema 与 value schema;z.enum()已覆盖原nativeEnum用法- object 的 strict、merge、unknown keys 行为与 intersection 合并冲突(现在可能直接抛错)需通过测试确认
ZodEffects、ZodTypeAny、AnyZodObject等 Zod 3 类型不应继续使用
表单引擎 API 调整
字段校验触发从四个validateOn*布尔项改为一个数组,submit 时始终校验:
// 旧写法已删除 validateOnBlur: true; validateOnChange: true; // 新写法:接收 'blur'、'change' 数组,默认两者都启用 validateOn?: readonly ('blur' | 'change')[];force/silent/validated-only这些 vee validation mode 在 TanStack runtime 中没有对应语义,已直接删除,使用它们的调用点需要改写。
回调签名发生了变化,业务代码中如有订阅要同步更新:
handleSubmit(values)→handleSubmit(values, rawValues),首参为格式化值,次参为同一次提交对应的只读原始快照;旧的单参数函数仍可直接使用handleValuesChange(values, fieldsChanged)→handleValuesChange(rawValues, fieldsChanged, getFormattedValues),第三个参数是惰性格式化函数,不调用时不产生深拷贝和转换开销getValues()返回 codec 编码后的TSubmitValues;getRawValues()返回未执行 codec 或旧格式化管道的独立快照;需要同时比较时用getValueSnapshot(),它返回{ rawValues, values }
规则注册入口从defineRules迁移到rules。新写法(setupVbenForm 实现中保留了旧入口的转发):
setupVbenForm({ rules: { required(value, _params, context) { const isEmpty = value === undefined || value === null || value === '' || (Array.isArray(value) && value.length === 0); return isEmpty ? `${context.label} is required` : true; }, }, });旧的defineRules仍会转发到同一个规则注册表,开发环境针对该弃用项只输出一次警告,生产环境不输出;若rules与defineRules提供同名规则,rules优先。
字段联动推荐使用dependencies.resolve(context):根据声明的triggerFields一次计算完整动态 patch 并原子更新字段状态,可以返回if、show、disabled、required、rules、componentProps、help和renderComponentContent;未返回rules时继续使用静态规则,显式返回rules: null时关闭静态规则。旧的if/show/disabled/required/rules/componentProps/trigger语法本轮仍完整兼容,但均已标记@deprecated,开发环境首次使用时警告一次;两种语法同时存在时以resolve为准。
方法名层面,新代码使用reset、submit、validateAndSubmit、clearValidation;旧的resetForm、submitForm、validateAndSubmitForm、resetValidate仍会委托给新实现,开发环境首次使用时给一次性 warning,生产环境静默。FormActions类型保留为FormContextApi的弃用别名。
第三步:按验收标准验证
文档给出的测试覆盖层级是:
- Zod 4 helper:default、optional、nullable、intersection、pipe、transform、coerce 与错误参数
- runtime:值读写、selector、reset、字段错误、validate 和异步校验
- 组件:输入绑定、blur/change 触发、错误消息、ARIA、dependencies 和数组增删
- 兼容:
rules/defineRules结果一致、开发 warning 去重、生产静默、类型别名 - 集成:
useVbenForm生命周期、提交、handleValuesChange、submit-on-change 和 async race
六条验收标准对应仓库根目录可执行的检查脚本:
# 1. 受影响 package、应用、playground 和 docs 无 TypeScript 错误 pnpm check:type # 2. form-ui 与所有应用构建成功 pnpm build # 3. 单元、组件和集成测试全部通过 pnpm test:unit # 4. 修改文件通过 oxfmt 与 ESLint pnpm lint剩余两条标准不绑定单一命令:
- 浏览器 smoke 流程中无
pageerror、console.error或未处理 Promise - 静态搜索中不再出现 vee 依赖、Zod 私有结构(
_def、_zod.def、typeName)或 Zod 3 错误参数(required_error、invalid_type_error)
最后一条静态搜索可以和第一步的git diff复查一起做:迁移是否彻底,以仓库中搜不到 vee 依赖和 Zod 3 私有写法为判断依据。
边界与已知限制
- codemod 工具无 dry-run 且原地改写文件,只能以"干净工作树 + 事后
git diff"来兜底,不要在有未提交改动时执行。 - 间接引入
z的 schema(经@vben/common-ui或应用 adapter)不在工具的可靠识别范围内,必须人工过一遍构造器错误参数、字符串格式和动态 refine 参数。 - 弃用项(
defineRules、dependencies旧语法、resetForm等)在开发环境只警告一次、生产环境完全静默,warning 消失不代表迁移完成,以静态搜索和类型检查为准。 .default().optional()、intersection 合并冲突等行为的判定标准是实际 parse 语义和测试通过,文档明确反对按类型名称或旧版经验猜测。
【免费下载链接】vue-vben-adminA modern vue admin panel built with Vue3, Shadcn UI, Vite, TypeScript, and Monorepo. It's fast!项目地址: https://gitcode.com/GitHub_Trending/vu/vue-vben-admin
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考