如何让Vue组件与v-model配合工作

在Vue.js 3.5版本中,您可以使用defineModel宏让Vue组件与v-model配合工作。例如:
  1. <!-- BaseInput.vue -->
  2. <script setup>
  3. const value = defineModel();
  4. </script>
  5. <template>
  6. <input type="text" v-model="value" />
  7. </template>

  8. <!-- App.vue -->
  9. <script setup>
  10. import { ref } from "vue";
  11. import BaseInput from "./components/BaseInput.vue";

  12. const value = ref("");
  13. </script>
  14. <template>
  15. <BaseInput v-model="value" />
  16. </template>
vue
在Vue.js 3.5以下版本中,defineModel宏不存在。相反,您可以使用modelValue属性来接收v-model的值,并发出update:modelValue事件来更新它。
  1. <!-- BaseInput.vue -->
  2. <script setup>
  3. const props = defineProps(["modelValue"]);
  4. const emit = defineEmits(["update:modelValue"]);
  5. </script>
  6. <template>
  7. <input
  8. type="text"
  9. :value="props.modelValue"
  10. @input="emit('update:modelValue', $event.target.value)"
  11. />
  12. </template>

  13. <!-- App.vue -->
  14. <script setup>
  15. import { ref } from "vue";
  16. import BaseInput from "./components/BaseInput.vue";

  17. const value = ref("");
  18. </script>
  19. <template>
  20. <BaseInput v-model="value" />
  21. </template>
vue
或者,您可以使用计算属性,其中getter返回props.modelValue,setter发出update:modelValue事件。
  1. <!-- BaseInput.vue -->
  2. <script setup>
  3. import { computed } from "vue";
  4. const props = defineProps(["modelValue"]);
  5. const emit = defineEmits(["update:modelValue"]);

  6. const value = computed({
  7. get: () => props.modelValue,
  8. set: (newValue) => emit("update:modelValue", newValue),
  9. });
  10. </script>
  11. <template>
  12. <input type="text" v-model="value" />
  13. </template>

  14. <!-- App.vue -->
  15. <script setup>
  16. import { ref } from "vue";
  17. import BaseInput from "./components/BaseInput.vue";

  18. const value = ref("");
  19. </script>
  20. <template>
  21. <BaseInput v-model="value" />
  22. </template>
vue
方法对比
1. defineModel(Vue 3.5+)
优点:
语法简洁,代码量最少
自动处理双向绑定
类型安全(TypeScript支持)
缺点:
仅支持Vue 3.5及以上版本
相对较新,社区资源较少
2. modelValue + emit(传统方法)
优点:
兼容性好,支持所有Vue 3版本
逻辑清晰,易于理解
社区资源丰富
缺点:
代码量较多
需要手动处理事件发射
3. computed属性方法
优点:
语法优雅,类似原生v-model
自动处理getter/setter
易于扩展和自定义
缺点:
需要导入computed
代码量适中
实际应用示例
自定义输入组件
  1. <!-- CustomInput.vue -->
  2. <script setup>
  3. import { computed } from "vue";

  4. const props = defineProps({
  5. modelValue: {
  6. type: String,
  7. default: ""
  8. },
  9. placeholder: {
  10. type: String,
  11. default: ""
  12. },
  13. type: {
  14. type: String,
  15. default: "text"
  16. }
  17. });

  18. const emit = defineEmits(["update:modelValue"]);

  19. const inputValue = computed({
  20. get: () => props.modelValue,
  21. set: (value) => emit("update:modelValue", value)
  22. });
  23. </script>

  24. <template>
  25. <div class="custom-input">
  26. <input
  27. :type="type"
  28. v-model="inputValue"
  29. :placeholder="placeholder"
  30. class="input-field"
  31. />
  32. </div>
  33. </template>

  34. <style scoped>
  35. .custom-input {
  36. position: relative;
  37. }

  38. .input-field {
  39. width: 100%;
  40. padding: 8px 12px;
  41. border: 1px solid #ddd;
  42. border-radius: 4px;
  43. font-size: 14px;
  44. }

  45. .input-field:focus {
  46. outline: none;
  47. border-color: #007bff;
  48. box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25);
  49. }
  50. </style>
vue
使用示例
  1. <!-- App.vue -->
  2. <script setup>
  3. import { ref } from "vue";
  4. import CustomInput from "./components/CustomInput.vue";

  5. const username = ref("");
  6. const email = ref("");
  7. </script>

  8. <template>
  9. <div class="form">
  10. <CustomInput
  11. v-model="username"
  12. placeholder="请输入用户名"
  13. type="text"
  14. />
  15. <CustomInput
  16. v-model="email"
  17. placeholder="请输入邮箱"
  18. type="email"
  19. />
  20. <div class="preview">
  21. <p>用户名: {{ username }}</p>
  22. <p>邮箱: {{ email }}</p>
  23. </div>
  24. </div>
  25. </template>

  26. <style scoped>
  27. .form {
  28. max-width: 400px;
  29. margin: 20px auto;
  30. padding: 20px;
  31. }

  32. .form > * {
  33. margin-bottom: 15px;
  34. }

  35. .preview {
  36. background: #f8f9fa;
  37. padding: 15px;
  38. border-radius: 4px;
  39. border: 1px solid #e9ecef;
  40. }
  41. </style>
vue
高级用法
1. 多v-model支持
  1. <!-- MultiInput.vue -->
  2. <script setup>
  3. const firstName = defineModel("firstName");
  4. const lastName = defineModel("lastName");
  5. </script>

  6. <template>
  7. <div class="multi-input">
  8. <input v-model="firstName" placeholder="名" />
  9. <input v-model="lastName" placeholder="姓" />
  10. </div>
  11. </template>
vue
2. 带验证的输入组件
  1. <!-- ValidatedInput.vue -->
  2. <script setup>
  3. import { computed } from "vue";

  4. const props = defineProps({
  5. modelValue: String,
  6. validator: {
  7. type: Function,
  8. default: () => true
  9. }
  10. });

  11. const emit = defineEmits(["update:modelValue", "validation"]);

  12. const inputValue = computed({
  13. get: () => props.modelValue,
  14. set: (value) => {
  15. const isValid = props.validator(value);
  16. emit("update:modelValue", value);
  17. emit("validation", { value, isValid });
  18. }
  19. });
  20. </script>

  21. <template>
  22. <input v-model="inputValue" />
  23. </template>
vue
最佳实践
1. 版本兼容性
  1. // 检查Vue版本并选择合适的方法
  2. const isVue35Plus = parseInt(Vue.version.split('.')[1]) >= 5;

  3. if (isVue35Plus) {
  4. // 使用 defineModel
  5. const value = defineModel();
  6. } else {
  7. // 使用传统方法
  8. const props = defineProps(["modelValue"]);
  9. const emit = defineEmits(["update:modelValue"]);
  10. }
JavaScript
2. TypeScript支持
  1. <!-- TypeScript版本 -->
  2. <script setup lang="ts">
  3. interface Props {
  4. modelValue: string;
  5. placeholder?: string;
  6. }

  7. interface Emits {
  8. (e: 'update:modelValue', value: string): void;
  9. }

  10. const props = withDefaults(defineProps<Props>(), {
  11. placeholder: ''
  12. });

  13. const emit = defineEmits<Emits>();
  14. </script>
vue
3. 性能优化
  1. <script setup>
  2. // 使用 shallowRef 优化大对象的性能
  3. import { shallowRef } from "vue";

  4. const value = shallowRef("");
  5. </script>
vue
常见问题
1. 为什么v-model不工作?
检查是否正确发出update:modelValue事件
确认props名称是否为modelValue
验证Vue版本兼容性
2. 如何处理复杂数据类型?
  1. <script setup>
  2. // 对于对象类型
  3. const props = defineProps({
  4. modelValue: {
  5. type: Object,
  6. default: () => ({})
  7. }
  8. });

  9. const emit = defineEmits(["update:modelValue"]);

  10. const updateValue = (key, value) => {
  11. const newValue = { ...props.modelValue, [key]: value };
  12. emit("update:modelValue", newValue);
  13. };
  14. </script>
vue
3. 如何实现自定义v-model名称?
  1. <!-- 使用 modelValue 作为默认名称 -->
  2. <script setup>
  3. const props = defineProps(["modelValue"]);
  4. const emit = defineEmits(["update:modelValue"]);
  5. </script>

  6. <!-- 或者使用 defineModel 指定名称 -->
  7. <script setup>
  8. const value = defineModel("customName");
  9. </script>
vue
总结
Vue组件与v-model的配合工作有三种主要方法:
defineModel(推荐) - Vue 3.5+,语法最简洁
modelValue + emit - 兼容性好,逻辑清晰
computed属性 - 语法优雅,易于扩展
选择合适的方法取决于您的Vue版本、项目需求和团队偏好。无论选择哪种方法,都要确保正确实现双向数据绑定,让组件能够无缝地与父组件通信。