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() ****让组件的双向绑定回归"直觉"------就像操作本地状态一样简单,却能自动同步到父组件。


相关推荐
跳动的梦想家h2 小时前
环境配置 + AI 提效双管齐下
java·vue.js·spring
夏幻灵2 小时前
HTML5里最常用的十大标签
前端·html·html5
Mr Xu_2 小时前
Vue 3 中 watch 的使用详解:监听响应式数据变化的利器
前端·javascript·vue.js
未来龙皇小蓝2 小时前
RBAC前端架构-01:项目初始化
前端·架构
程序员agions2 小时前
2026年,微前端终于“死“了
前端·状态模式
万岳科技系统开发2 小时前
食堂采购系统源码库存扣减算法与并发控制实现详解
java·前端·数据库·算法
程序员猫哥_3 小时前
HTML 生成网页工具推荐:从手写代码到 AI 自动生成网页的进化路径
前端·人工智能·html
龙飞053 小时前
Systemd -systemctl - journalctl 速查表:服务管理 + 日志排障
linux·运维·前端·chrome·systemctl·journalctl
我爱加班、、3 小时前
Websocket能携带token过去后端吗
前端·后端·websocket
AAA阿giao3 小时前
从零拆解一个 React + TypeScript 的 TodoList:模块化、数据流与工程实践
前端·react.js·ui·typescript·前端框架