Vue3搭配Vuelidate2时v$.FormData.name.$model不更新问题排查
为何
v$.FormData.name.$model无法随表单输入同步更新? 问题背景
近期在将Vue 2应用升级至Vue 3的过程中遇到Vuelidate运行异常:
v$.FormData.name.$model始终仅保留初始值,不会随用户输入同步更新v$.FormData.$model.name可以获取到最新的表单值,但该路径下不包含Vuelidate的校验属性与方法
当前已将表单控件改造为使用<script setup>语法的SFC单文件组件,父子组件仍采用Options API编写,尝试过直接将v$.FormData.name.$model作为v-model绑定值传入组件,问题仍未解决。
核心实现代码
const FormData = reactive({}); const rules = reactive(Validations); // { FormData : { name: {}... const v$ = useVuelidate(rules, { FormData }, { $autoDirty: true, $lazy: true }); ... watch( FormData, (oldValue, newValue) => { v$.value.FormData[control.vModel].$touch(); emit("event_form_updated", { attributeName: props.control.vModel, model: v$.value.FormData[control.vModel], }); }, { deep: true } ); ...
<span> <component :is="components[control.component]" :control="control" :options="control.options" :values="control.value" v-model="FormData[control.vModel]" :v="v$.FormData[control.vModel]" v-show="showCtrl(control)" :tab-index="100 * (index + 1)" /> </span>
问题原因
核心问题出在FormData初始化时传入的是空对象:
Vuelidate在useVuelidate执行初始化阶段,会遍历传入的state对象的已有属性,为每个匹配校验规则的字段生成独立的响应式$model代理,实现值和校验状态的双向同步。如果字段是初始化完成后才动态添加到FormData上的,Vuelidate不会自动为这些后加的字段生成独立的$model代理,只会在顶层$model(也就是整个FormData的代理)上同步最新值,就会出现单字段路径下$model不更新、顶层路径能拿到值的现象。
修复方案
- 优先方案:初始化
FormData时提前声明所有表单字段的初始值,哪怕是空字符串、空数组这类默认值,保证Vuelidate初始化时能识别到所有字段,为每个字段生成正常同步的$model代理。示例:const FormData = reactive({ name: '', // 把Validations里FormData下配置的所有字段都在这里提前声明初始值 }) - 最佳实践调整:表单组件的v-model直接绑定对应校验节点的
$model,替代绑定原始FormData属性。开启$autoDirty: true配置后,通过$model更新值会自动标记字段脏状态,可以删掉原有watch里手动调用$touch()的冗余逻辑。调整后的组件绑定写法:<component :is="components[control.component]" :control="control" :options="control.options" :values="control.value" v-model="v$.FormData[control.vModel].$model" :v="v$.FormData[control.vModel]" v-show="showCtrl(control)" :tab-index="100 * (index + 1)" /> - 动态字段兼容方案:如果业务场景确实需要动态增减表单字段、无法提前声明所有字段,就不要混用路径取值:读取表单最新值统一走
v$.FormData.$model[字段名],读取校验方法、校验状态属性统一走v$.FormData[字段名]即可。
内容的提问来源于stack exchange,提问作者Nathan
相关产品推荐
相关产品推荐

