defineModel 是 Vue 3.4 引入的一个组合式语法糖,用于更简洁地在子组件中 声明并使用 v-model 绑定的 props 和事件,是对传统 props + emit('update:modelValue') 模式的封装。
defineModel 是在子组件中声明 v-model 的 语法糖,用于代替 props 和 emit 的手动处理。
用法演示 👀
<!-- 父组件 -->
<MyInput v-model="msg" />
<!-- 子组件:MyInput.vue -->
<script setup>
const model = defineModel() // 相当于 props.modelValue + emit('update:modelValue')
</script>
<template>
<input v-model="model" />
</template>
是不是比以前的写法简单多了?
Vue3.4 之前怎么写 🔧
// 子组件
defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])
const model = computed({
get: () => props.modelValue,
set: (val) => emit('update:modelValue', val)
})
defineModel的本质原理🧠
Vue 编译器在编译 script setup 时,会自动将 defineModel() :
-
生成一个 props.modelValue 的访问 getter
-
生成一个 emit('update:modelValue') 的 setter
-
自动构造 computed,并返回一个响应式绑定
相当于你写了下面这些东西:
const props = defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])
const model = computed({
get: () => props.modelValue,
set: (val) => emit('update:modelValue', val)
})
多个 v-model(命名 model)🧩
<!-- 父组件 -->
<MyInput v-model:title="title" v-model:content="content" />
// 子组件
const title = defineModel('title')
const content = defineModel('content')
Vue 会自动映射到:
- props.title / props.content
- emit('update:title') / emit('update:content')
类型支持 💡
你可以显式指定类型:
const model = defineModel<string>()
const title = defineModel<string>('title')
注意事项 ⚠️
| 项目 | 说明 |
|---|---|
| Vue 版本 | defineModel 仅在 Vue 3.4+ 中可用 |
| 编译器支持 | 依赖 Vue SFC 的编译器支持,不能在普通 JS 文件中使用 |
| 必须配合 <script setup> | 不能在普通 <script> 中使用 |
| 不是运行时 API | 它是编译期语法糖,运行时并不存在 defineModel 函数 |
代码分析 🧐
可以借助 Vue 官方提供的 SFC Playground 编译结果来看 defineModel 的编译产物。
编译后的 setup 等效代码(简化模拟)⚙️
export default {
props: {
modelValue: {
type: String,
}
},
emits: ['update:modelValue'],
setup(props, { emit }) {
const model = computed({
get: () => props.modelValue,
set: (value) => emit('update:modelValue', value)
})
return {
model
}
}
}
你可以看到:
- defineModel() 被转化为对 props.modelValue 的 computed 包装;
- 自动注册了 props.modelValue 和 emits.update:modelValue;
- 最终返回的 model 变量,是可以直接绑定在 v-model="model" 上的响应式引用。
export default {
props: {
title: String,
content: String
},
emits: ['update:title', 'update:content'],
setup(props, { emit }) {
const title = computed({
get: () => props.title,
set: (val) => emit('update:title', val)
})
const content = computed({
get: () => props.content,
set: (val) => emit('update:content', val)
})
return {
title,
content
}
}
}
深入理解:defineModel 的本质 🛠
defineModel(name?: string) =>
computed({
get: () => props[name || 'modelValue'],
set: (val) => emit(`update:${name || 'modelValue'}`, val)
})
并且它隐式注册了 props 和 emits,无需你手动写。