Vue3.6 都来了,v-model 的 "新玩法" 还有这么多人不会!
@zs.duan
Vue3.6 都来了,v-model 的 "新玩法" 还有这么多人不会!
阅读量:394
2025-08-15 14:20:43

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: { typeStringdefault'hello' }
})
const emit  = defineEmits(['update:modelValue'])
const model = computed({
  get() => props.modelValue,
  setval => 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'张三',
  age18,
  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
zs.duan@qq.com
微信
微信
地址
重庆市沙坪坝
微信小程序
微信小程序
我的标签
小程序
harmonyOS
HTML
微信小程序
javaSrcipt
typeSrcipt
vue
uniapp
nodejs
react
防篡改
nginx
mysql
请求加解密