Vue3.6 都来了,v-model 的 "新玩法" 还有这么多人不会!
“Vue3.6
已经进入 Alpha 阶段,可我司同事还在问我:『为什么 v-model 不灵?』”
别笑,说的可能就是你。
Vue3.4 早在 2023 年 12 月就把 defineModel
转正,可直到今天,还有人用 Vue2 时代的 props + emit
手写双向绑定,代码又长又臭,Bug 一堆。
本文一次性帮你扫清所有盲点:
-
从 为什么诞生 到 底层原理 -
从 单 v-model 到 多 v-model、修饰符、TypeScript -
从 现场避坑 到 团队落地清单
看完就能在明天的需求评审里优雅地说:“这个功能我用 3 行代码就能搞定。”
defineModel 到底是什么?
一句话定义:让子组件像原生 <input>
一样直接支持 v-model
的语法糖; 说白了就是一个 宏(macro),在编译期把 defineModel()
展开成 props
+ emit
宏 VS 函数
-
宏:编译期代码生成,运行时 0
额外开销。 -
函数:运行时真实调用。
因此defineModel
不需要import
,也不能在普通<script>
或.js/.ts
文件里使用。
生成的等价代码
// 你写的
const model = defineModel<string>({ default: 'hello' })
// 编译后(伪代码)
const props = defineProps({
modelValue: { type: String, default: 'hello' }
})
const emit = defineEmits(['update:modelValue'])
const model = computed({
get: () => props.modelValue,
set: val => emit('update:modelValue', val)
})
快速上手(3 个例子包会)
以下示例全部基于
<script setup>
,可直接复制到*.vue
文件运行。
单 v-model —— 最常用 90% 场景
-
父组件
<template>
<UserName v-model="name" />
<p>父组件拿到的值:{{ name }}</p>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import UserName from './UserName.vue'
const name = ref('张三')
</script>
-
子组件 UserName.vue
<template>
<input v-model="modelValue" />
</template>
<script setup lang="ts">
const modelValue = defineModel<string>()
// 等价于 const modelValue = defineModel<string>({ required: true })
</script>
多个 v-model —— 表单类组件刚需
-
父组件
<template>
<UserForm
v-model:name="form.name"
v-model:age="form.age"
v-model:phone="form.phone"
/>
<pre>{{ form }}</pre>
</template>
<script setup lang="ts">
import { reactive } from 'vue'
import UserForm from './UserForm.vue'
const form = reactive({
name: '张三',
age: 18,
phone: '13800138000'
})
</script>
-
子组件 UserForm.vue
<template>
<input v-model="name" placeholder="姓名" />
<input v-model="age" placeholder="年龄" />
<input v-model="phone" placeholder="手机号" />
</template>
<script setup lang="ts">
const name = defineModel<string>('name')
const age = defineModel<number>('age')
const phone = defineModel<string>('phone')
</script>
带修饰符 & 转换器 —— 再也不用手动 .trim
-
父组件
<template>
<TrimInput v-model.trim="keyword" />
<p>父组件值:{{ keyword }}</p>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import TrimInput from './TrimInput.vue'
const keyword = ref('')
</script>
-
子组件 TrimInput.vue
<template>
<input v-model="modelValue" />
</template>
<script setup lang="ts">
const [modelValue, modifiers] = defineModel<string, 'trim'>()
// 当父组件写 v-model.trim 时,modifiers.trim === true
if (modifiers.trim) {
// 通过 set 函数实时转换
}
</script>
-
如果你需要 实时转换,用 get / set
:
const [modelValue, modifiers] = defineModel<string, 'trim'>({
set(val) {
return modifiers.trim ? val.trim() : val
}
})
TypeScript 高阶姿势
|
|
---|---|
|
defineModel<string>({ required: true }) |
|
defineModel<string>({ default: '张三' }) |
|
defineModel<'male' | 'female'>() |
|
defineModel<User>() |
-
注意:默认值如果是 对象
/数组
,请用函数返回新实例,避免引用共享:
defineModel<string[]>({ default: () => ['A', 'B'] })
Vue3.6 都要来了,如果你还在手写 props + emit
做双向绑定,赶紧把这篇文章甩进群里,下班前让全组学会 defineModel
!
一个小前端
我是一个小前端

zs.duan@qq.com



重庆市沙坪坝


我的标签
小程序
harmonyOS
HTML
微信小程序
javaSrcipt
typeSrcipt
vue
uniapp
nodejs
react
防篡改
nginx
mysql
请求加解密
还没有人评论 快来占位置吧