v-model 的本质,defineModel() 是 Vue 3.4 的重大改进

Vue的v-model本质上是:modelValue + @update:modelValue的语法糖。


显式写法适用于教学演示、需要透传多个v-model或添加额外逻辑的场景。


Vue3.4+引入的defineModel()新特性简化了双向绑定,自动处理props/emits,支持类型推导和修饰符,比传统写法更简洁高效。


推荐在Vue3.4+项目中使用defineModel(),它减少了代码量并提升可读性,是v-model的最佳实践方案。


v-model 的本质

v-model 其实是语法糖 ,它会自动展开为 :modelValue + @update:modelValue

html 复制代码
<!-- 这两种写法完全等价 -->
<BaseInput v-model="username" />
<BaseInput :modelValue="username" @update:modelValue="val => username = val" />

为什么示例中显式写出展开形式?

1. 展示透传机制

html 复制代码
<template>
  <div class="custom-input">
    <label>{{ label }}</label>
    <!-- 显式写出可以清楚看到属性透传和事件透传 -->
    <BaseInput 
      v-bind="$attrs"
      :modelValue="modelValue" 
      @update:modelValue="$emit('update:modelValue', $event)"
    />
  </div>
</template>

<script setup>
defineProps(['label', 'modelValue'])
defineEmits(['update:modelValue'])
</script>

如果用 v-model 简写:

html 复制代码
<!-- 看起来简洁,但不够直观 -->
<BaseInput v-bind="$attrs" v-model="modelValue" />

2. 需要同时透传多个 v-model

Vue 3 支持多个 v-model,显式写法更灵活:

html 复制代码
<template>
  <CustomForm 
    v-model:firstName="firstName"
    v-model:lastName="lastName"
    v-model:email="email"
  />
</template>

<!-- CustomForm 组件内部 -->
<template>
  <div>
    <Input 
      :modelValue="firstName"
      @update:modelValue="$emit('update:firstName', $event)"
    />
    <Input 
      :modelValue="lastName"
      @update:modelValue="$emit('update:lastName', $event)"
    />
  </div>
</template>

3. 需要在更新时添加额外逻辑

html 复制代码
<template>
  <BaseInput 
    :modelValue="modelValue"
    @update:modelValue="handleUpdate"
  />
</template>

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

const handleUpdate = (val) => {
  // 添加验证、格式化等额外逻辑
  const formatted = val.trim().toLowerCase()
  emit('update:modelValue', formatted)
}
</script>

什么时候用哪种写法?

场景 推荐写法 原因
简单透传 v-model 简洁清晰
需要验证/转换 显式展开 便于插入逻辑
多个 v-model 显式展开 更明确每个绑定
教学示例 显式展开 展示底层机制
$attrs 混用 显式展开 避免冲突,更可控

实际项目中的写法

<script setup> 中,如果想在 JavaScript 逻辑中访问 props,必须显式接收。

代码优化:以一个编辑器(EasyMDE)组件为例,实现其在Vue不同版本下(父子组件数据双向绑定)的最佳实践(附:防抖 Debounce 优化)

最推荐:使用 defineModel


示例中显式写出 :modelValue + @update:modelValue 是为了让代码更教学化透明化,实际开发中可以根据场景灵活选择。


defineModel() 是 Vue 3.4+ 引入的新特性,让 v-model 的双向绑定变得更加简洁。


官网内容


defineModel()


这个宏可以用来声明一个双向绑定 prop,通过父组件的 v-model 来使用。组件 v-model 指南中也讨论了示例用法。


在底层,这个宏声明了一个 model prop 和一个相应的值更新事件。如果第一个参数是一个字符串字面量,它将被用作 prop 名称;否则,prop 名称将默认为 "modelValue"


在这两种情况下,你都可以再传递一个额外的对象,它可以包含 prop 的选项和 model ref 的值转换选项。


javascript 复制代码
// 声明 "modelValue" prop,由父组件通过 v-model 使用
const model = defineModel()
// 或者:声明带选项的 "modelValue" prop
const model = defineModel({ type: String })

// 在被修改时,触发 "update:modelValue" 事件
model.value = "hello"

// 声明 "count" prop,由父组件通过 v-model:count 使用
const count = defineModel("count")
// 或者:声明带选项的 "count" prop
const count = defineModel("count", { type: Number, default: 0 })

function inc() {
  // 在被修改时,触发 "update:count" 事件
  count.value++
}

完整功能对比

特性 传统写法 defineModel()
代码量 多(需手动声明 props/emits) 少(自动生成)
类型安全 需手动定义类型 自动推导,支持泛型
默认值 props 中定义 通过参数配置
修饰符 手动处理 自动支持 .trim
可读性 较复杂 简洁直观

版本要求

版本 defineModel() 支持
Vue 3.3 及以下 ❌ 不支持
Vue 3.4 ✅ 正式引入(实验性)
Vue 3.5+ ✅ 稳定支持

总结defineModel() 是 Vue 3.4 的重大改进,大幅简化了 v-model 的使用,让代码更简洁、类型更安全。如果你的项目使用 Vue 3.4+,强烈推荐使用这个新特性!

相关推荐
西西学代码2 小时前
Flutter---构造函数
开发语言·javascript·flutter
invicinble2 小时前
关于对vue的认识
javascript·vue.js·ecmascript
EF@蛐蛐堂2 小时前
【vue】Vite 生态 5 个 “新玩具“
前端·javascript·vue.js
风之舞_yjf2 小时前
Vue基础(29)_props配置项、ref属性
前端·vue.js
Fairy要carry3 小时前
项目03-手搓Agent之团队协作(发消息/分配任务)
linux·前端·python
hzb666663 小时前
xd_day32-day40
java·javascript·学习·安全·web安全·tomcat·php
东北甜妹3 小时前
Python脚本
java·开发语言·前端
四千岁3 小时前
WSL + OpenCode 最佳实践:环境一致、模型配置、GUI 远程使用
前端·javascript·后端
你挚爱的强哥3 小时前
【sgCreateQrcode】自定义组件:模仿草料二维码做了一个简单的二维码制作组件
javascript·vue.js·elementui