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

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

  7. <script setup>
  8. import { ref } from "vue";
  9. import BaseInput from "./components/BaseInput.vue";

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

  12. <script setup>
  13. import { ref } from "vue";
  14. import BaseInput from "./components/BaseInput.vue";

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

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

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

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

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

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

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

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

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

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

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

  4. const username = ref("");
  5. const email = ref("");
  6. script>

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

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

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

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

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

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

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

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

  20. <template>
  21. <input v-model="inputValue" />
  22. 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. <script setup lang="ts">
  2. interface Props {
  3. modelValue: string;
  4. placeholder?: string;
  5. }

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

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

  12. const emit = defineEmits<Emits>();
  13. 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. <script setup>
  2. const props = defineProps(["modelValue"]);
  3. const emit = defineEmits(["update:modelValue"]);
  4. script>

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