v-model 在vue2与vue3中的差别

v-model 在vue2与vue3中的差别

Vue 2 与 Vue 3 中 v-model 的核心差异主要体现在语法设计、功能扩展和底层实现上。以下是两者的详细对比:


一、默认绑定行为

1. Vue 2

语法糖v-model 默认绑定到 value 属性,监听 input 事件。
编译结果

  <ChildComponent v-model="data" />
  <!-- 等价于 -->
  <ChildComponent :value="data" @input="data = $event" />

自定义组件:需通过 model 选项指定 propevent

  // 子组件配置
  export default {
    model: { prop: 'checked', event: 'change' },
    props: ['checked'],
    methods: { emitChange() { this.$emit('change', newValue); } }
  }

2. Vue 3

语法糖升级:默认绑定到 modelValue 属性,监听 update:modelValue 事件。
编译结果

  <ChildComponent v-model="data" />
  <!-- 等价于 -->
  <ChildComponent :modelValue="data" @update:modelValue="data = $event" />

无需额外配置:子组件直接通过 definePropsdefineEmits 声明。


二、v-model 绑定支持

1. Vue 2

限制:每个组件仅支持单个 v-model,需通过 .sync 修饰符实现多属性双向绑定:

  <ChildComponent :title.sync="title" :content.sync="content" />

2. Vue 3

原生支持多绑定:通过 v-model:propName 语法实现:

  <UserForm v-model:name="userName" v-model:age="userAge" />

子组件处理

  // 子组件声明
  const props = defineProps(['name', 'age']);
  const emit = defineEmits(['update:name', 'update:age']);

三、自定义组件配置差异

1. Vue 2

依赖 model 选项:需显式指定 prop 和事件名。
示例

  export default {
    model: { prop: 'selected', event: 'change' },
    props: ['selected'],
    methods: { handleChange() { this.$emit('change', newValue); } }
  }

2. Vue 3

直接声明:使用 Composition API 的 definePropsdefineEmits

  // 子组件声明
  const props = defineProps(['modelValue']);
  const emit = defineEmits(['update:modelValue']);

四、事件处理与参数传递

1. Vue 2

固定事件名:仅支持 input 事件,需通过 model 选项自定义事件名。
参数限制:事件默认传递单个值(如 $event 为直接数据)。

2. Vue 3

灵活事件名:事件名为 update:propName,支持任意参数。
示例

  // 子组件触发事件
  emit('update:modelValue', newValue);

五、性能与语法简洁性

Vue 2 痛点
• 多属性绑定需依赖 .sync,语法冗余。
• 无法直接监听复杂数据结构(如 MapSet)的变化。

Vue 3 优化
Tree Shaking 支持:未使用的 v-model 相关代码不会被打包。
更简洁的语法:通过 v-model:propName 直接绑定,无需额外配置。


总结对比表

特性Vue 2Vue 3
默认绑定value + input 事件modelValue + update:modelValue 事件
多绑定支持.sync 修饰符原生支持 v-model:propName
自定义组件配置model 选项直接通过 defineProps/defineEmits
事件灵活性固定 input 事件自定义 update:propName 事件
语法简洁性冗余配置较多语法统一,配置简洁

使用场景建议

Vue 2 项目
• 单数据流场景直接使用 v-model
• 多属性绑定使用 .sync

Vue 3 项目
• 优先使用 v-model:propName 实现多属性绑定。
• 结合 Composition API 提升代码可维护性。

通过以上对比,Vue 3 的 v-model 在设计上更灵活、功能更强大,显著提升了开发效率和代码可读性。


vue 3.4 的v-model

Vue 3.4 对 v-model 的实现进行了重大升级,引入 defineModel作为双向绑定的官方推荐方案。以下是基于最新规范的核心用法和底层机制解析:


一、核心语法升级:defineModel

1. 基础用法

<!-- 子组件 Child.vue -->
<script setup>
const model = defineModel() // 默认绑定 modelValue 属性和 update:modelValue 事件
</script>

<template>
  <input v-model="model" />
</template>

父组件只需直接使用 v-model

  <Child v-model="count" />

底层机制defineModel 会被编译为 modelValue prop 和 update:modelValue 事件。

2. 多参数绑定

支持为不同属性绑定多个 v-model

<!-- 父组件 -->
<Child v-model:title="title" v-model:content="content" />

<!-- 子组件 -->
<script setup>
const title = defineModel('title')
const content = defineModel('content')
</script>

• 通过 v-model:param 指定参数名,子组件用 defineModel('param') 接收。


二、高级配置与验证

1. Prop 选项配置

const model = defineModel({
  type: String,
  required: true,
  default: 'Hello'
})

• 支持标准 prop 验证规则(如类型校验、必填项等)。

2. 修饰符处理

<!-- 父组件使用修饰符 -->
<Child v-model.uppercase.trim="text" />

<!-- 子组件处理修饰符 -->
<script setup>
const [model, modifiers] = defineModel({
  set(value) {
    return modifiers.uppercase ? value.toUpperCase() : value
  }
})
</script>

• 通过解构 modifiers 对象识别 .trim.number 或自定义修饰符。


三、复杂数据类型的默认值

针对对象/数组类型需特殊处理:

const model = defineModel({
  default: () => ({}),
  get(val) {
    return reactive(val || {}) // 确保默认值为响应式对象
  }
})

注意:直接给复杂类型设置 default: [] 会导致多个实例共享同一引用。


四、与传统写法的对比

Vue 3.4 之前

<script setup>
const props = defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])
</script>

<template>
  <input 
    :value="props.modelValue"
    @input="emit('update:modelValue', $event.target.value)"
  />
</template>

缺点:需手动声明 props 和 emits,代码冗余。

Vue 3.4 优势

代码简洁性:减少 50% 模板代码;
类型安全:自动推导 TypeScript 类型;
维护性:统一响应式变量操作接口。


五、底层原理与注意事项

  1. Ref 特性
    defineModel() 返回的 model 是一个 Ref 对象,直接修改 .value 会触发父组件更新。

  2. 响应式同步
    • 子组件修改 model.value → 触发 update:modelValue 事件 → 父组件更新值;
    • 父组件值变化 → 自动同步到子组件的 model.value

  3. 性能优化
    通过编译时静态分析生成高效代码,减少运行时开销。


六、迁移建议

  1. 旧项目升级
    • 使用 defineModel 逐步替换 defineProps + defineEmits 组合;
    • 复杂场景可用 computed 包装实现中间逻辑。

  2. 常见问题
    Q: 直接修改 model.value 可能导致 props 污染?
    A: defineModel 内部已隔离处理,无需担心。


总结对比表

特性Vue 3.4 (defineModel)Vue 3.4 前传统写法
代码量1 行声明需 props + emits 声明(3+行)
多参数支持原生支持 v-model:param需手动配置多个 prop/emit
类型推导自动推断 TypeScript 类型需手动标注类型
维护成本低(统一接口)高(分散逻辑)

通过 defineModel,Vue 3.4 实现了双向绑定逻辑的标准化和工程化,显著提升开发效率和代码可维护性。

评论