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
        
            请求加解密
        

还没有人评论 快来占位置吧