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

相关推荐
梦曦i1 分钟前
RouterLink v2.5.0:H5端原生能力全面回归
前端·uni-app
小林ixn15 分钟前
React + Zustand + JWT:从零实现登录鉴权与请求拦截
前端·react.js·前端框架
葡萄城技术团队19 分钟前
下拉多选类型单元格:在 SpreadJS 里接入 xm\-select
前端
粥里有勺糖43 分钟前
视野修炼-技术周刊第130期 | 光影效果
前端·javascript·github
小岛前端1 小时前
AI Skills 已经封神,但新的问题却越来越严重!
前端·后端·github
拖孩1 小时前
一个人 + AI 做的小程序,上线 15 天赚了 10 块 5
前端·后端·微信小程序
用户93983829415991 小时前
安卓自动化脚本热更新怎么做?不重新打包 APK 也能更新代码
前端
全栈项目管理程序猿1 小时前
ArcGIS JS 基础教程(13):屏幕坐标与地理坐标互转
javascript
猫七先生1 小时前
在 React 里用 PMTiles 自托管底图
前端
tanghonghanhaoli1 小时前
【图书翻译】有意义的游戏设计:桌面游戏的方法论与心理学(第1、2章)
前端·游戏·ui