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+,强烈推荐使用这个新特性!

相关推荐
最逗前端小白鼠1 天前
vue3 数据响应式遇到的问题
前端·vue.js
倚栏听风雨1 天前
ts中 ?? 和 || 区别
前端
冴羽1 天前
请愿书:Node.js 核心代码不应该包含 AI 代码!
前端·javascript·node.js
我家猫叫佩奇1 天前
一款灵感源自《集合啦!动物森友会》的 UI 组件库
前端
mmmmm123421 天前
深入 DOM 查询底层:HTMLCollection 动态原理与 querySelectorAll 静态快照解析
前端·javascript
淸湫1 天前
前端JavaScript:数据类型、实例对象 、内置对象、构造函数之间的关系
javascript
weixin199701080161 天前
《TikTok 商品详情页前端性能优化实战》
前端·性能优化
闲坐含香咀翠1 天前
告别二次登录!Web端检测并唤起Electron客户端实战
前端·客户端
岁月宁静1 天前
都知道AI大模型能生成文本内容,那你知道大模型是怎样生成文本的吗?
前端·vue.js·人工智能
别看我只是一直狼1 天前
从观察者模式到 RxJS:让复杂的异步逻辑变得优雅又舒服
javascript