You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在vee-validate中结合使用Vue 3.4的defineModel?

如何在vee-validate中结合Vue 3.4的defineModel实现表单字段关联(避免重复watch)

你可以通过以下几种更简洁的方式实现defineModel与vee-validate字段的关联,无需为每个字段手动编写watch:


方案1:计算属性双向桥接(单字段推荐)

用计算属性作为中间层,同时同步defineModel和vee-validate的字段ref,自动处理双向更新,代码更简洁集中。

<template>
  <form class="flex flex-col gap-6 m-6">
    <div class="flex flex-col gap-2">
      <label for="businessName">Business Name</label>
      <InputText
        id="businessName"
        v-model="businessNameSync"
        v-bind="businessNameAttrs"
      />
      <div class="text-xs text-red-500">
        {{ errors.businessName }}
      </div>
    </div>

    <Button @click="handleClick">Validate</Button>
  </form>
</template>

<script setup lang="ts">
import { computed } from 'vue';
import { z } from 'zod';
import { toTypedSchema, useForm } from 'vee-validate';

// 组件的v-model字段
const businessName = defineModel<string>('businessName');

const formSchema = toTypedSchema(
  z.object({
    businessName: z
      .string({ required_error: 'Business Name is required' })
      .min(1, { message: 'Business Name is required' }),
  })
);

const { errors, defineField, validate } = useForm({
  validationSchema: formSchema,
});

// 获取vee-validate的字段ref和属性
const [businessNameVee, businessNameAttrs] = defineField('businessName');

// 双向同步defineModel与vee-validate字段
const businessNameSync = computed({
  get() {
    return businessName.value;
  },
  set(val) {
    businessName.value = val;
    businessNameVee.value = val;
  },
});

const handleClick = () => {
  validate();
};
</script>

方案2:批量字段同步(多字段表单推荐)

如果表单字段较多,可以通过遍历字段列表实现批量双向同步,避免重复代码:

<template>
  <form class="flex flex-col gap-6 m-6">
    <div class="flex flex-col gap-2">
      <label for="businessName">Business Name</label>
      <InputText
        id="businessName"
        v-model="formModel.businessName"
        v-bind="fieldAttrs.businessName"
      />
      <div class="text-xs text-red-500">
        {{ errors.businessName }}
      </div>

      <label for="email">Email</label>
      <InputText
        id="email"
        v-model="formModel.email"
        v-bind="fieldAttrs.email"
      />
      <div class="text-xs text-red-500">
        {{ errors.email }}
      </div>
    </div>

    <Button @click="handleClick">Validate</Button>
  </form>
</template>

<script setup lang="ts">
import { watch } from 'vue';
import { z } from 'zod';
import { toTypedSchema, useForm } from 'vee-validate';

// 组件的对象类型v-model
const formModel = defineModel<{ businessName: string; email: string }>();

const formSchema = toTypedSchema(
  z.object({
    businessName: z.string().min(1, 'Business Name is required'),
    email: z.string().email('Invalid email format'),
  })
);

const { errors, defineField, validate, values } = useForm({
  validationSchema: formSchema,
  initialValues: formModel.value,
});

// 批量定义字段并存储属性
const fieldAttrs: Record<string, any> = {};
const fieldRefs: Record<string, any> = {};
['businessName', 'email'].forEach(key => {
  const [ref, attrs] = defineField(key);
  fieldRefs[key] = ref;
  fieldAttrs[key] = attrs;
});

// 同步defineModel到vee-validate
watch(formModel, (newVal) => {
  Object.entries(newVal).forEach(([key, val]) => {
    fieldRefs[key].value = val;
  });
}, { deep: true });

// 同步vee-validate到defineModel(按需开启)
watch(values, (newVal) => {
  Object.assign(formModel.value, newVal);
}, { deep: true });

const handleClick = () => {
  validate();
};
</script>

方案3:直接将defineModel作为vee-validate数据源(最彻底)

让vee-validate直接使用defineModel作为表单模型,无需额外同步逻辑,本质上是把defineModel包装成符合vee-validate要求的对象结构:

<template>
  <form class="flex flex-col gap-6 m-6">
    <div class="flex flex-col gap-2">
      <label for="businessName">Business Name</label>
      <InputText
        id="businessName"
        v-model="businessName"
        v-bind="businessNameAttrs"
      />
      <div class="text-xs text-red-500">
        {{ errors.businessName }}
      </div>
    </div>

    <Button @click="handleClick">Validate</Button>
  </form>
</template>

<script setup lang="ts">
import { z } from 'zod';
import { toTypedSchema, useForm } from 'vee-validate';

const businessName = defineModel<string>('businessName');

const formSchema = toTypedSchema(
  z.object({
    businessName: z.string().min(1, 'Business Name is required'),
  })
);

// 包装defineModel为vee-validate可识别的模型结构
const formModel = {
  get businessName() {
    return businessName.value;
  },
  set businessName(val) {
    businessName.value = val;
  },
};

const { errors, defineField, validate } = useForm({
  validationSchema: formSchema,
  model: formModel,
  initialValues: { businessName: businessName.value },
});

const [, businessNameAttrs] = defineField('businessName');

const handleClick = () => {
  validate();
};
</script>

内容的提问来源于stack exchange,提问作者TinyTiger

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 21:36:13