vue3中使用defineModel

是的!从 Vue 3.4 开始,Vue 官方引入了 defineModel() 编译宏(macro),极大简化了组件中实现 v-model 双向绑定的写法 ,无需手动声明 propsemit,也无需处理 modelValue / update:modelValue 的样板代码。

下面系统讲解 defineModel() 的使用方式、原理、优势和注意事项。


一、基础用法:替代 modelValue + emit

传统写法(Vue 3.0 ~ 3.3)

xml 复制代码
<!-- Child.vue -->
<script setup>
  const props = defineProps(['modelValue'])
  const emit = defineEmits(['update:modelValue'])

  function update() {
    emit('update:modelValue', props.modelValue + 1)
  }
</script>

<template>
  <div>{{ props.modelValue }}</div>
  <button @click="update">+</button>
</template>

Vue 3.4+ 新写法: defineModel()

xml 复制代码
<!-- Child.vue -->
<script setup>
  const model = defineModel() // 返回一个 ref

  function update() {
    model.value++ // 直接修改,自动同步到父组件
  }
</script>

<template>
  <div>Parent bound v-model is: {{ model }}</div>
  <button @click="update">Increment</button>
</template>

父组件完全不变:

xml 复制代码
<template>
  <Child v-model="count" />
</template>

<script setup>
  import { ref } from 'vue'
  const count = ref(0)
</script>

model 是一个 双向绑定的 ref

  • 读取 model.value → 获取父组件传入的值
  • 修改 model.value → 自动触发 update:modelValue,更新父组件数据

二、支持带参数的 v-model (多模型绑定)

Vue 支持多个 v-model,例如:

xml 复制代码
<Parent>
  <Child v-model:name="name" v-model:age="age" />
</Parent>

使用 defineModel 实现:

xml 复制代码
<!-- Child.vue -->
<script setup>
  const name = defineModel('name')
  const age = defineModel('age')

  // 或者用对象形式(可选)
  // const { name, age } = defineModels({ name: String, age: Number })
</script>

<template>
  <input v-model="name" />
  <input v-model.number="age" />
</template>

注意:defineModel('propName') 会自动对应 v-model:propName


三、类型与默认值(TypeScript / 运行时校验)

1. 指定类型(TypeScript)

c 复制代码
const model = defineModel<string>()
// model.value 类型为 string | undefined

2. 设置默认值

arduino 复制代码
const model = defineModel({ default: 'hello' })

3. 运行时校验 + 默认值

php 复制代码
const model = defineModel({
  type: String,
  required: false,
  default: 'default text'
})

💡 这些选项会自动转换为等效的 ****props ****声明,由 Vue 编译器处理。


四、与 useAttrs() 协同工作

虽然 defineModel() 自动处理了 modelValue,但其他属性仍需透传:

xml 复制代码
<script setup>
  const model = defineModel()
  // 如果有多个根节点,或想控制透传位置,才需要 useAttrs
</script>

<template>
  <!-- 单根节点:自动透传 attrs(包括 class/style/@focus 等) -->
  <input v-model="model" />
</template>

如果组件有多个根节点 ,必须手动使用 v-bind="$attrs",否则 Vue 会警告。


五、总结:为什么推荐 defineModel()

对比项 传统方式 defineModel()
代码量 多(props + emits) 极简(一行)
易错性 容易拼错 update:modelValue 零错误
可读性 逻辑分散 聚焦数据流
封装效率 高(尤其包装原生元素)
TypeScript 支持 需手动标注 自动推导

一句话
defineModel() ****让组件的双向绑定回归"直觉"------就像操作本地状态一样简单,却能自动同步到父组件。


相关推荐
山楂树の43 分钟前
图像标注大坑:img图片 + Canvas 叠加标注,同步放大后标注位置偏移、对不齐?详解修复方案及亚像素处理原理
前端·css·学习·canva可画
本山德彪1 小时前
我做了一个拼豆图纸生成器,把照片秒变图纸
前端
DTrader1 小时前
用TS无法实盘量化? - 实盘均线策略
前端·api
进击的夸父1 小时前
vfojs:Vue 超集架构,外壳React灵魂Vue
前端
编程老船长1 小时前
解决不同项目需要不同 Node.js 版本的问题
前端·vue.js
Wect1 小时前
LeetCode 5. 最长回文子串:DP + 中心扩展
前端·算法·typescript
漫游的渔夫1 小时前
前端开发者做 Agent:别写成一次请求,用 5 步受控循环防止 AI 乱跑
前端·人工智能·typescript
kyriewen3 小时前
Webpack vs Vite:一个是“老黄牛”,一个是“猎豹”,你选谁?
前端·webpack·vite
打小就很皮...3 小时前
html2canvas + jsPDF 生成 PDF 的踩坑与解决方案总结
前端·pdf
全栈前端老曹3 小时前
【前端地图】多地图平台适配方案——高德、百度、腾讯、Google Maps SDK 差异对比、封装统一地图接口
前端·javascript·百度·dubbo·wgs84·gcj-02·bd09