当前位置: 首页 > news >正文

【vue3】v-model 的 “新玩法“

前言

Vue3.6 已经进入 Alpha 阶段,Vue3.4 早在 2023 年 12 月就把 defineModel 转正式了!

defineModel 是 Vue 3.3+ 中引入的一个编译器宏(Compile-time Macro),用于简化组件中 v-model的双向绑定实现。它解决了传统 v-model 实现中需要手动声明 props 和 emit 的繁琐问题,让代码更简洁。

defineModel宏简化了这一过程。它会返回一个ref,这个ref可以像普通的数据ref一样使用,但会自动处理prop和事件。

自动实现 v-model 的双向绑定

声明一个响应式的 ref;自动注册对应的 prop(如 modelValue);自动注册对应的事件(如 update:modelValue

defineModel 是什么

一句话定义:让子组件像原生 <input> 一样直接支持 v-model 的语法糖; 说白了就是一个 宏(macro),在编译期把 defineModel() 展开成 props + emit

特性:

1. 自动连接prop和事件:内部自动声明了`modelValue` prop和`update:modelValue`事件。

2. 支持修饰符:可以处理`v-model`的修饰符(如`.trim`, `.number`)。

如果父组件使用修饰符,例如`v-model.trim`,那么修饰符会以对象的形式传递给`defineModel`的第二个参数。我们可以根据修饰符对值进行处理。

3. 支持多个`v-model`:通过指定参数,如`defineModel('foo')`,可以用于多个`v-model`绑定(例如`v-model:foo`)。

宏 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)
    })

    注意:基于 <script setup>,可直接复制到 *.vue 文件运行。

传统实现 vs defineModel

方式传统实现defineModel
代码量需要手动声明 props + emit + 事件处理一行声明
数据更新需调用 emit('update:modelValue', value)直接给 ref 赋值
修饰符需通过 props.modelModifiers 手动处理在 setter 中自动处理
类型安全需额外类型声明支持泛型类型

单 v-model 

父组件

<template><UserName v-model="name" /><p>父组件拿到的值:{{ name }}</p>
</template><script setup lang="ts">
import { ref } from 'vue'
import UserName from './Names.vue'const name = ref('名称')
</script>

子组件 Names.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><UserFormv-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>

子组件

<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'] })

注意事项

版本要求:Vue ≥ 3.3,且需配置构建工具:

// vite.config.js
export default {plugins: [vue({script: {defineModel: true // 启用宏}})]
}

本质是语法糖:编译后会展开为:

// defineModel() 编译结果
const modelValue = defineModel()
// ↓ 编译后 ↓
const modelValue = ref(props.modelValue)
watch(modelValue, (v) => emit('update:modelValue', v))

修饰符处理:当父组件使用 v-model.trim 时,子组件可通过 defineModel 的 set 选项处理。

总结

defineModel 是 Vue 组件双向绑定的趋势,它:

✅ 减少样板代码(无需手动处理 props/emit)

✅ 提供更直观的响应式体验

✅ 完美支持 TypeScript

✅ 简化修饰符处理

http://www.dtcms.com/a/329383.html

相关文章:

  • 基于人工智能和物联网融合跌倒监控系统(LW+源码+讲解+部署)
  • 面试实战 问题二十六 JDK 1.8 核心新特性详解
  • 猫头虎AI分享:Word MCP,让AI具备Word文档操作能力,文档创建、内容添加、格式编辑等AI能力
  • 【Word VBA Zotero 引用宏错误分析与改正指南】【解决[21–23]参考文献格式插入超链接问题】
  • 基于Java的Markdown转Word工具(标题、段落、表格、Echarts图等)
  • Linux 桌面到工作站的“性能炼金术”——开发者效率的 6 个隐形瓶颈与破解方案
  • 一条n8n工作流
  • vscode+phpstudy+xdebug如何调试php
  • windows10装Ubuntu22.04系统(双系统)
  • VS2022+Qt 5.15.2+FreeCAD 0.21.2开发环境配置流程
  • C# WPF本地Deepseek部署
  • 洛谷 P2607 [ZJOI2008] 骑士-提高+/省选-
  • M4T无人机在外墙脱落监测中的应用:从被动补救到主动预防的技术革新
  • 【代码随想录day 19】 力扣 450.删除二叉搜索树中的节点
  • 从原材料到成品,光模块 PCB 制造工艺全剖析
  • hutool 作为http 客户端工具调用的一点点总结
  • PG靶机 - PayDay
  • pt-online-schema-change 全解析:MySQL 表结构变更的安全之道
  • 编程的几点感悟
  • 【工具】雀语queyu文件批量下载 文档内容复刻导出
  • LeetCode 面试经典 150_数组/字符串_整数转罗马数字(18_12_C++_中等)(模拟)(对各位进行拆解)
  • 地球磁层全球MHD模型中模拟Dst指数的半经验方法
  • 在RHEL 9.X上安装 Docker最新版(28.3.3)
  • 嵌入式|VNC实现开发板远程Debian桌面
  • Spring 源码学习(十)—— DispatcherServlet
  • 专题:2025抖音电商与微短剧行业研究报告|附150+份报告PDF汇总下载
  • 小迪23年-32~40——java简单回顾
  • Hive 创建事务表的方法
  • 机器学习-----DBSCAN算法
  • 进阶向:Python编写自动化邮件发送程序