v-model 在 Vue2 和 Vue3 中的实现对比或异同

Vue2和Vue3的v-model实现对比显示:语法保持兼容但底层机制优化。


Vue3将默认属性名从value改为modelValue,事件名从input变为update:modelValue,并原生支持多v-model绑定(替代Vue2的.sync修饰符)。


自定义修饰符处理增强,通过modelModifiers访问。


迁移时需调整prop和事件命名,多绑定改用v-model:propName形式。


Vue3的改进使表单绑定更灵活直观,同时保持与Vue2的语法兼容性。


Vue3对v-model机制进行了优化升级。Vue3的改进包括支持类型检查、多绑定和增强修饰符处理,提升了开发体验。


v-model 在 Vue 2 和 Vue 3 中的实现对比

对比维度 Vue 2 Vue 3
基本语法 v-model="value" v-model="value" (语法不变)
底层原理 v-model = :value + @input v-model = :modelValue + @update:modelValue
组件上的默认属性名 value modelValue
组件上的默认事件名 input update:modelValue
多 v-model 绑定 不支持,需使用 .sync 修饰符 原生支持:v-model:propName="value"
自定义修饰符 有限支持 增强支持,可通过 modelModifiers 访问
修饰符示例 .lazy, .number, .trim (内置) 保留内置修饰符,并支持自定义修饰符处理
组件内实现 通过 model 选项配置 prop 和 event 通过 propsemits 声明,默认使用 modelValue
示例代码 vue <ChildComponent v-model="pageTitle" /> vue <ChildComponent v-model="pageTitle" />
示例代码(组件内) js model: { prop: 'title', event: 'change' }, props: ['title'] js props: ['modelValue'], emits: ['update:modelValue']
多绑定示例 vue <ChildComponent :title.sync="pageTitle" :content.sync="pageContent" /> vue <ChildComponent v-model:title="pageTitle" v-model:content="pageContent" />
自定义修饰符处理 较复杂,需通过计算属性等方法 通过 modelModifiers prop 访问,如 props.modelModifiers.capitalize

关键变化总结

  1. 兼容性 :Vue 3 中 v-model 的语法与 Vue 2 保持基本一致,确保迁移的平顺性

  2. 默认属性/事件名变更 :从 value/input 变为 modelValue/update:modelValue

  3. 多 v-model 支持 :Vue 3 原生支持多个 v-model 绑定,替代 Vue 2 的 .sync 修饰符

  4. 自定义修饰符增强:Vue 3 提供了更直观的方式处理自定义修饰符

  5. .sync 修饰符 :Vue 2 的 .sync 修饰符在 Vue 3 中被移除,其功能由多 v-model 语法替代


迁移建议

  • Vue 2 组件使用 v-model 时,在 Vue 3 中需要将 value prop 改为 modelValue,将 input 事件改为 update:modelValue

  • Vue 2 中使用 .sync 修饰符的地方,在 Vue 3 中应改为 v-model:propName 形式

  • 自定义修饰符的处理逻辑需要适应 Vue 3 的新 API


Vue 2 确实默认每个组件只能有一个 v-model


但有两种方式可以实现多个双向数据绑定:


1. 默认的单个 v-model(2.2.0+)

默认情况下,组件的 v-model 会使用 value 属性和 input 事件:

vue

复制代码
<!-- 父组件 -->
<ChildComponent v-model="pageTitle" />

<!-- 相当于 -->
<ChildComponent :value="pageTitle" @input="pageTitle = $event" />

<!-- 子组件 -->
<script>
export default {
  props: ['value'],
  methods: {
    updateValue(newValue) {
      this.$emit('input', newValue)
    }
  }
}
</script>

2. 实现多个 "v-model" 的两种方式

方式一:使用 .sync 修饰符(推荐)

.sync 修饰符可以实现多个双向绑定:

vue

复制代码
<!-- 父组件 -->
<ChildComponent 
  :title.sync="pageTitle" 
  :content.sync="pageContent"
/>

<!-- 子组件 -->
<script>
export default {
  props: ['title', 'content'],
  methods: {
    updateTitle(newTitle) {
      this.$emit('update:title', newTitle)
    },
    updateContent(newContent) {
      this.$emit('update:content', newContent)
    }
  }
}
</script>

方式二:自定义 v-model(不常用)

可以在组件中配置 model 选项来修改默认的 prop 和 event:

javascript

复制代码
// 子组件
export default {
  model: {
    prop: 'checked',
    event: 'change'
  },
  props: ['checked']
}

3. Vue 3 的改进

Vue 3 原生支持多个 v-model:

vue

复制代码
<!-- Vue 3 -->
<UserName
  v-model:first-name="firstName"
  v-model:last-name="lastName"
/>

总结

虽然 Vue 2 默认只支持一个 v-model,但通过 .sync 修饰符可以轻松实现多个双向绑定。在实际开发中,.sync 是处理多个双向数据绑定的标准做法。


Vue3 modelValue onUpdate:modelValue 使用示例


在 Vue 3 中,modelValue 是默认的 v-model prop 名称,update:modelValue 是默认的事件名称。


1. 基础使用示例

子组件:CustomInput.vue

vue

复制代码
<template>
  <div>
    <input 
      :value="modelValue" 
      @input="$emit('update:modelValue', $event.target.value)"
    />
    <p>当前值:{{ modelValue }}</p>
  </div>
</template>

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

父组件使用:

vue

复制代码
<template>
  <CustomInput v-model="text" />
  <p>父组件值:{{ text }}</p>
</template>

<script setup>
import { ref } from 'vue'
import CustomInput from './CustomInput.vue'

const text = ref('')
</script>

2. 使用 Options API 的示例

子组件:

vue

复制代码
<template>
  <input 
    :value="modelValue" 
    @input="handleInput"
  />
</template>

<script>
export default {
  name: 'CustomInput',
  props: ['modelValue'],
  emits: ['update:modelValue'],
  methods: {
    handleInput(event) {
      this.$emit('update:modelValue', event.target.value)
    }
  }
}
</script>

3. 带类型定义和验证的示例

vue

复制代码
<template>
  <div>
    <input 
      type="number"
      :value="modelValue"
      @input="handleInput"
      :min="min"
      :max="max"
    />
  </div>
</template>

<script setup>
import { defineProps, defineEmits } from 'vue'

const props = defineProps({
  modelValue: {
    type: Number,
    required: true,
    default: 0
  },
  min: {
    type: Number,
    default: 0
  },
  max: {
    type: Number,
    default: 100
  }
})

const emit = defineEmits(['update:modelValue'])

const handleInput = (event) => {
  let value = parseInt(event.target.value) || 0
  
  // 限制范围
  if (value < props.min) value = props.min
  if (value > props.max) value = props.max
  
  emit('update:modelValue', value)
}
</script>

4. 多个 v-model 绑定示例

子组件:UserForm.vue

vue

复制代码
<template>
  <div>
    <input 
      :value="firstName" 
      @input="$emit('update:firstName', $event.target.value)"
      placeholder="First Name"
    />
    <input 
      :value="lastName" 
      @input="$emit('update:lastName', $event.target.value)"
      placeholder="Last Name"
    />
  </div>
</template>

<script setup>
defineProps({
  firstName: String,
  lastName: String
})

defineEmits(['update:firstName', 'update:lastName'])
</script>

父组件使用多个 v-model:

vue

复制代码
<template>
  <UserForm
    v-model:firstName="firstName"
    v-model:lastName="lastName"
  />
  <p>全名:{{ firstName }} {{ lastName }}</p>
</template>

<script setup>
import { ref } from 'vue'
import UserForm from './UserForm.vue'

const firstName = ref('')
const lastName = ref('')
</script>

5. 带修饰符的 v-model

子组件:TrimInput.vue

vue

复制代码
<template>
  <input 
    :value="modelValue" 
    @input="handleInput"
  />
</template>

<script setup>
import { defineProps, defineEmits } from 'vue'

const props = defineProps({
  modelValue: String,
  modelModifiers: { // 自动接收修饰符
    default: () => ({})
  }
})

const emit = defineEmits(['update:modelValue'])

const handleInput = (event) => {
  let value = event.target.value
  
  // 处理修饰符
  if (props.modelModifiers.trim) {
    value = value.trim()
  }
  
  if (props.modelModifiers.capitalize) {
    value = value.charAt(0).toUpperCase() + value.slice(1)
  }
  
  emit('update:modelValue', value)
}
</script>

父组件使用修饰符:

vue

复制代码
<template>
  <TrimInput v-model.trim="text1" />
  <TrimInput v-model.trim.capitalize="text2" />
</template>

<script setup>
import { ref } from 'vue'
import TrimInput from './TrimInput.vue'

const text1 = ref('')
const text2 = ref('')
</script>

6. 使用 computed 实现双向绑定的更复杂示例

vue

复制代码
<template>
  <div>
    <input 
      :value="displayValue"
      @input="updateValue($event.target.value)"
    />
  </div>
</template>

<script setup>
import { computed, defineProps, defineEmits } from 'vue'

const props = defineProps({
  modelValue: String
})

const emit = defineEmits(['update:modelValue'])

// 使用 computed 处理显示值
const displayValue = computed({
  get() {
    return props.modelValue || ''
  },
  set(value) {
    emit('update:modelValue', value)
  }
})

// 或者直接使用方法
const updateValue = (value) => {
  // 可以在这里添加自定义逻辑
  const processedValue = value.toUpperCase() // 示例:转为大写
  emit('update:modelValue', processedValue)
}
</script>

关键点总结:

  1. 默认 prop : modelValue

  2. 默认事件 : update:modelValue

  3. 语法糖简化:

    vue

    复制代码
    <Child v-model="data" />
    <!-- 等价于 -->
    <Child :modelValue="data" @update:modelValue="data = $event" />
  4. 多个 v-model : 使用 v-model:propName 语法

  5. 修饰符 : 通过 modelModifiers prop 接收


这种设计让 Vue 3 的双向绑定更加灵活和类型安全。

相关推荐
心.c2 小时前
Vue3+Node.js实现文件上传并发控制与安全防线 进阶篇
前端·javascript·vue.js·安全·node.js
pas1362 小时前
36-mini-vue nextTick
前端·javascript·vue.js
VX:Fegn08952 小时前
计算机毕业设计|基于springboot + vue教务管理系统(源码+数据库+文档)
vue.js·spring boot·课程设计
Irene19913 小时前
Vue3中 <slot >不支持 ref 属性的替代方案
vue.js·ref
多仔ヾ3 小时前
Vue.js 前端开发实战之 10-网络请求和 UI 组件库
vue.js
多仔ヾ4 小时前
Vue.js 前端开发实战之 09-服务器端渲染
vue.js
计算机学姐4 小时前
基于SpringBoot的校园跑腿系统【数据可视化统计+原创精品】
java·vue.js·spring boot·后端·mysql·信息可视化·echarts
Beginner x_u6 小时前
前端八股文 Vue下
前端·vue.js·状态模式
HWL567914 小时前
获取网页首屏加载时间
前端·javascript·vue.js