vue 3.4 带来了一个很实用的新功能:defineModel。这个功能让 v-model 双向绑定的代码变得特别简洁。以前需要写很多行的代码,现在一行就能搞定。
在 Vue 3.3 及之前的版本中,我们要在子组件里实现 v-model 功能,需要写不少代码。
比如我们要做一个自定义输入框组件:
<!-- CustomInput.vue -->
<script setup>
// 1. 先定义 props
const props = defineProps({
modelValue: {
type: String,
default: ''
}
})
// 2. 再定义 emits
const emit = defineEmits(['update:modelValue'])
// 3. 写事件处理函数
const handleInput = (event) => {
// 4. 手动触发更新
emit('update:modelValue', event.target.value)
}
</script>
<template>
<input
:value="modelValue" <!-- 5. 绑定值 -->
@input="handleInput" <!-- 6. 绑定事件 -->
placeholder="请输入内容"
/>
</template>
可以看到,为了实现双向绑定,我们需要完成 6 个步骤。这还只是一个 v-model,如果要支持多个 v-model,代码就更复杂了。
比如用户信息表单组件:
<!-- UserForm.vue -->
<script setup>
// 每个属性都要定义 props
const props = defineProps({
firstName: String,
lastName: String,
email: String
})
// 每个属性都要定义 emits
const emit = defineEmits([
'update:firstName',
'update:lastName',
'update:email'
])
// 每个属性都要写处理函数
const updateFirstName = (e) => emit('update:firstName', e.target.value)
const updateLastName = (e) => emit('update:lastName', e.target.value)
const updateEmail = (e) => emit('update:email', e.target.value)
</script>
<template>
<div class="user-form">
<input :value="firstName" @input="updateFirstName" placeholder="姓" />
<input :value="lastName" @input="updateLastName" placeholder="名" />
<input :value="email" @input="updateEmail" placeholder="邮箱" />
</div>
</template>
这种写法不仅麻烦,还容易出错。有时候可能会写错事件名,或者忘记触发更新。
现在有了 defineModel,一切都变得简单了。同样的功能,代码量大大减少。
单个 v-model 的情况:
<!-- CustomInput.vue -->
<script setup>
// 只需要这一行
const modelValue = defineModel()
</script>
<template>
<input
v-model="modelValue" <!-- 直接使用 v-model -->
placeholder="请输入内容"
/>
</template>
多个 v-model 的情况:
<!-- UserForm.vue -->
<script setup>
// 每个 v-model 都是一行代码
const firstName = defineModel('firstName')
const lastName = defineModel('lastName')
const email = defineModel('email')
</script>
<template>
<div class="user-form">
<input v-model="firstName" placeholder="姓" />
<input v-model="lastName" placeholder="名" />
<input v-model="email" placeholder="邮箱" />
</div>
</template>
父组件的用法跟以前完全一样:
<script setup>
import { ref } from 'vue'
import CustomInput from './CustomInput.vue'
import UserForm from './UserForm.vue'
const inputValue = ref('初始值')
const firstName = ref('张')
const lastName = ref('三')
const email = ref('zhangsan@example.com')
</script>
<template>
<!-- 单个 v-model -->
<CustomInput v-model="inputValue" />
<p>当前值: {{ inputValue }}</p>
<!-- 多个 v-model -->
<UserForm
v-model:firstName="firstName"
v-model:lastName="lastName"
v-model:email="email"
/>
</template>
defineModel 不只是简化代码,它还支持一些高级功能。
<script setup>
const title = defineModel({
type: String,
default: '默认标题'
})
const count = defineModel('count', {
type: Number,
default: 0,
required: true
})
</script>
Vue 的 v-model 支持修饰符,比如 .trim、.number。用 defineModel 也可以处理自定义修饰符。
<script setup>
// 定义带修饰符的 model
const modelValue = defineModel({
set(value) {
// 如果有 uppercase 修饰符,就转成大写
if (modelValue.modifiers.uppercase) {
return value.toUpperCase()
}
return value
}
})
</script>
<template>
<input v-model="modelValue" />
</template>
在父组件中使用:
<template>
<!-- 使用自定义修饰符 -->
<CustomInput v-model.uppercase="textValue" />
</template>
defineModel 也可以在组合式函数中使用,这让代码复用更加方便。
<script setup>
// 封装一个带验证的 model
function useValidatedModel(initialValue, validator) {
const model = defineModel({
default: initialValue
})
const error = ref('')
watch(model, (newValue) => {
const result = validator(newValue)
if (result !== true) {
error.value = result
} else {
error.value = ''
}
})
return {
model,
error
}
}
// 使用
const { model: username, error: usernameError } = useValidatedModel('', (value) => {
if (value.length < 3) return '用户名至少3个字符'
return true
})
</script>
如果你有现有的项目,想要改用 defineModel,迁移过程很简单。可以一个一个组件慢慢改,不用一次性全部迁移。
迁移步骤:
删除 defineProps 中的 modelValue 定义
删除 defineEmits 中的 update:modelValue 定义
添加 const modelValue = defineModel()
把模板中的 :value="modelValue" @input="handleInput" 改成 v-model="modelValue"
举个例子,把之前的 CustomInput 组件迁移到新写法:
迁移前:
<script setup>
const props = defineProps({
modelValue: String
})
const emit = defineEmits(['update:modelValue'])
const handleInput = (e) => emit('update:modelValue', e.target.value)
</script>
<template>
<input :value="modelValue" @input="handleInput" />
</template>
迁移后:
<script setup>
const modelValue = defineModel()
</script>
<template>
<input v-model="modelValue" />
</template>
新的项目:直接使用 defineModel
老的项目:可以逐步迁移,优先修改经常使用的组件
团队项目:统一约定使用方式,保持代码一致性
版本要求:需要 Vue 3.4 或更高版本
类型支持:如果你用 TypeScript,defineModel 有很好的类型推断
浏览器兼容:和 Vue 3 的浏览器支持保持一致
defineModel 在性能上和原来的写法没有明显差别。它主要是在编译时进行转换,运行时的表现跟原来差不多。
Vue 3.4 的 defineModel 确实让双向绑定的代码变得更加简洁。从原来需要写很多行代码,到现在只需要一行,这大大提高了开发效率。
这个改进不仅减少了代码量,也让代码更容易理解和维护。特别是对于新手来说,学习成本降低了,不用再记那些复杂的 props 和 emits 写法。
如果你还在用老版本的 Vue,可以考虑升级到 3.4 来体验这个新功能。如果你已经开始用 Vue 3.4,那么现在就可以在项目中使用 defineModel 来简化你的代码了。
好的工具应该让开发变得更简单,defineModel 正是这样一个好工具。它保留了 Vue 易用性的特点,同时让代码更加简洁明了。
本文内容仅供个人学习/研究/参考使用,不构成任何决策建议或专业指导。分享/转载时请标明原文来源,同时请勿将内容用于商业售卖、虚假宣传等非学习用途哦~感谢您的理解与支持!
前端的视图层和数据层有时需要实现双向绑定(two-way-binding),例如mvvm框架,数据驱动视图,视图状态机等,研究了几个目前主流的数据双向绑定框架,总结了下。目前实现数据双向绑定主要有以下三种。
vue.js 采用数据劫持的方式,结合发布者-订阅者模式,通过Object.defineProperty()来劫持各个属性的setter,getter以监听属性的变动,在数据变动时发布消息给订阅者,触发相应的监听回调.
vue框架目前在前端开发使用比较广了,但是又很多同学发现vue创建对象之后添加新的属性实现不了双向绑定,下面就简单介绍如何解决vue双向绑定出现失效的问题
用到 contenteditable=true的 div ,而在这个 div 上是使用 v-model 是没有效果的,这里的双向数据绑定该如何实现?解决思路:一自定义指令,二使用组件。
v-model 双向绑定实际上就是通过子组件中的 $emit 方法派发 input 事件,父组件监听 input 事件中传递的 value 值,并存储在父组件 data 中;然后父组件再通过 prop 的形式传递给子组件 value 值,再子组件中绑定 input 的 value 属性即可。
当我们学习angular或者vue的时候,其双向绑定为我们开发带来了诸多便捷,今天我们就来分析一下vue双向绑定的原理。vue.js 则是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter
首先我们来看一些,vue的基本使用方法;然后我们根据使用方法,来设计一个类和一些基础的声明周期;获取根元素;生命周期 beforeCreate;获取初始数据;获取渲染函数
用过Vue的同学都知道,Vue里<input> 、 <textarea> 及 <select>等表单元素可以通过v-model指令实现双向数据绑定,也就是说,当用户通过交互改变表单的值时,表单绑定的数据也会同步响应,这一点也是Vue对开发人员非常友好的点之一。
绑定确实是个有趣的话题。现在我的绑定器有了不少的功能1. 附着在Object对象上,一切以对象为中心2. 与页面元素进行双向绑定
无论在任何的语言或框架中,我们都提倡代码的复用性。对于Vue来说也是如此,相同的代码逻辑会被封装成组件,除了复用之外,更重要的是统一管理提高开发效率。我真就接手过一个项目,多个页面都会用到的列表
内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!