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

相关推荐
weixin_382395239 小时前
为小工厂量身打造:本地部署的物料管理系统带缺料计算
前端·制造
__zRainy__9 小时前
解决pnpm v10+不自动构建
前端·pnpm·工程化
猫猫不是喵喵.9 小时前
Vue3 Props 属性
前端·javascript·vue.js
醉城夜风~10 小时前
CSS元素显示模式(display)
前端·css
AI大模型-小华11 小时前
Codex 三方充值快速入门指南
java·前端·数据库·chatgpt·ai编程·codex·chatgpt pro
做前端的娜娜子13 小时前
同一链接实现 PC Web 与移动 H5 自适应
前端·掘金·金石计划
小帅不太帅13 小时前
架构没变、规模没变,DeepSeek V4 Flash 正式版凭什么暴涨 47 分?
前端·aigc·deepseek
jarvisuni14 小时前
DeepSeekFlash前端依旧拉垮,而且变慢了很多!
前端·javascript·算法
卷福同学15 小时前
AI编程出海第二步:验证关键词能否做站
前端·人工智能·后端