Vue 3 useModel vs defineModel:选择正确的双向绑定方案
📖 概述
useModel()
是 Vue 3.4+ 版本中引入的一个组合式 API 辅助函数,它是驱动defineModel()
的底层实现。这个函数主要用于在非单文件组件中实现双向数据绑定,特别是在使用原始的setup()
函数时。
⚠️ 重要提示:如果使用 <script setup>
,应当优先使用 defineModel()
。
🎯 基本概念
什么是 useModel?
useModel()
是一个用于创建双向数据绑定的底层辅助函数。它允许子组件直接修改父组件传递的 prop 值,而无需手动触发 emit 事件。
与 defineModel 的关系
特性 | defineModel() | useModel() |
---|---|---|
使用场景 | <script setup> 语法糖 | 底层实现 |
代码简洁度 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ |
控制精细度 | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
推荐程度 | 应当优先使用 | 特定场景使用 |
- ✅
defineModel()
是<script setup>
中的语法糖,更简洁易用,应当优先使用 - 🔧
useModel()
是底层实现,适用于非单文件组件或需要更精细控制的场景
🔧 函数签名
function useModel(props: Record<string, any>,key: string,options?: DefineModelOptions
): ModelRef;type DefineModelOptions<T = any> = {get?: (v: T) => any;set?: (v: T) => any;
};type ModelRef<T, M extends PropertyKey = string, G = T, S = T> = Ref<G, S> &[ModelRef<T, M, G, S>, Record<M, true | undefined>];
📋 参数说明
参数 | 类型 | 必需 | 描述 |
---|---|---|---|
props | Record<string, any> | ✅ | 组件的 props 对象 |
key | string | ✅ | 要绑定的 prop 名称 |
options | DefineModelOptions | ❌ | 自定义 getter 和 setter 函数 |
🎯 使用场景
1️⃣ 非单文件组件中的双向绑定
当使用传统的
setup()
函数而不是<script setup>
时,useModel()
是创建双向绑定的最佳选择。在<script setup>
中,请优先使用defineModel()
。
2️⃣ 需要自定义转换逻辑
当需要对传入和传出的值进行转换时,可以使用
options
参数。
3️⃣ 复杂的组件库开发
在开发可复用的组件库时,可能需要更精细的控制。
💻 代码示例
🚀 基础用法
export default {props: ["count"],emits: ["update:count"],setup(props) {const count = useModel(props, "count");// 直接修改值会自动触发 emitcount.value = 1;return {count,};},
};
🔄 带自定义转换的用法
export default {props: ["value"],emits: ["update:value"],setup(props) {const modelValue = useModel(props, "value", {// 自定义 getter:将字符串转换为数字get: (v) => (v ? parseInt(v) : 0),// 自定义 setter:将数字转换为字符串set: (v) => v.toString(),});return {modelValue,};},
};
🎨 在模板中使用
<template><div><input :value="count" @input="count = $event.target.value" /><button @click="count++">增加</button></div>
</template><script>
export default {props: ["count"],emits: ["update:count"],setup(props) {const count = useModel(props, "count");return {count,};},
};
</script>
⚖️ 与 defineModel 的对比
✅ defineModel (推荐用于 script setup)
<script setup>
const count = defineModel("count");
</script>
🔧 useModel (适用于 setup 函数)
export default {props: ["count"],emits: ["update:count"],setup(props) {const count = useModel(props, "count");return { count };},
};
⚠️ 注意事项
🔢 版本要求
- 🚫 仅在 Vue 3.4+ 版本中可用
- ✅ 确保项目依赖版本满足要求
📝 Props 和 Emits 声明
与 defineModel()
不同,使用 useModel()
时需要手动声明:
- 📦 props 数组或对象
- 📤 emits 数组或对象
🛡️ 类型安全
在 TypeScript 项目中,建议为 props 和 emits 提供完整的类型定义:
interface Props {count: number;
}interface Emits {"update:count": [value: number];
}export default {props: ["count"] as PropType<Props>,emits: ["update:count"] as Emits,setup(props: Props) {const count = useModel(props, "count");return { count };},
};
🎯 最佳实践
1️⃣ 优先使用 defineModel
在
<script setup>
中,应当优先使用defineModel()
,代码更简洁易读。 只有在以下情况下才考虑使用useModel()
:
- 🔧 使用传统的
setup()
函数 - ⚙️ 需要更精细的控制和自定义转换逻辑
- 📚 开发复杂的组件库
2️⃣ 合理使用自定义转换
只在确实需要数据转换时使用
options
参数,避免过度复杂化。
3️⃣ 保持一致性
在同一个项目中,保持双向绑定的实现方式一致。
4️⃣ 类型安全
在 TypeScript 项目中,始终提供完整的类型定义。
❓ 常见问题
Q: 什么时候使用 useModel 而不是 defineModel?
A: 在
<script setup>
中,应当优先使用defineModel()
。 只有在以下情况下才使用useModel()
:
- 🔧 使用传统的
setup()
函数而不是<script setup>
- ⚙️ 需要自定义 getter/setter 进行复杂的数据转换
- 🎛️ 需要更精细的控制逻辑
Q: useModel 是否支持多个双向绑定?
A: 是的,可以多次调用
useModel()
来创建多个双向绑定。
Q: 如何处理复杂的验证逻辑?
A: 可以在
options.set
中添加验证逻辑,如果验证失败可以抛出错误或返回原值。
📝 总结
useModel()
是 Vue 3.4+ 中实现双向数据绑定的强大工具,特别适用于非单文件组件。重要原则:在<script setup>
中,应当优先使用defineModel()
,代码更简洁易读。 只有在使用传统的setup()
函数或需要更精细控制时,才考虑使用useModel()
。理解这两个 API 的使用场景和差异,有助于在 Vue 3 项目中更好地实现组件间的数据通信。
Vue 3 useModel vs defineModel:选择正确的双向绑定方案 - 高质量源码分享平台-免费下载各类网站源码与模板及前沿技术分享