为什么会需要使用defineModel()
注意:defineModel() 需要在3.4及以上版本才可使用;
组件之间通讯,通过 props 和 emits
进行通讯,是单向数据流,比如:props
是自上而下的(父组件数据修改导致子组件更新,而自己不能修改父组件传入的 props
属性),而emits是自下而上的(子组件通过事件触发父组件事件);
defineModel()
返回的值是一个 ref
。
它可以像其他 ref 一样被访问以及修改,不过它能起到在父组件和当前变量之间的双向绑定的作用:
defineModel() 实现原理
:defineModel()
的双向绑定是在编译之后,创建了一个model
的ref
变量以及一个modelValue的props
,并且watch
了props
中的modelValue
;当子组件中的modelValue
更新时,会触发update:modelValue
事件,当父组件接收到这个事件时候,同时更新父组件的变量;
它的 .value 和父组件的 v-model 的值同步;
当它被子组件变更了,会触发父组件绑定的值一起更新。
1、defineModel() 的双向绑定:
父组件:
<template>
<div class="my-define-module">
This is a defineModel text page.
// 使用v-model 绑定person对象
<ChildMy v-model="person"/>
<hr>
{{ person.name }}---{{ person.age }}
</div>
</template>
<script setup>
import ChildMy from './child.vue'
import { ref } from 'vue'
const person = ref({
name: 'Andy',
age: 11
})
</script>
子组件
<template>
<div class="my-define-module">
child -- {{person.name}} // 第一次打印的是父组件传递过来的 Andy
<el-button type="primary" @click="updatedName">child btn</el-button>
</div>
</template>
<script setup>
import { ref, reactive } from 'vue'
// defineModel() 返回的是一个ref对象
const person = defineModel({
name: 'child',
age: 18
})
console.log('==person==child=', person.value)
const updatedName = () => {
// 子组中更新person 属性,会同时更新父组件的person属性
person.value.name = `${person.value.name}+$`
person.value.age = person.value.age + 1
}
</script>
2、defineModel() 创建多个v-model
注意:需要都是基本类型,不能是引用类型,否子组件读到的是undefined
如图:
父组件:
<template>
<div class="my-define-module">
This is a defineModel text page.
<!-- <ChildMy v-model="person"/> -->
// 传入多个v-model时的person是Object,导致子组件中person无法通过defineModel获取
<ChildMy v-model:person="person" v-model:job="job" v-model:num="num"/>
<hr>
{{ person.name }}---{{ person.age }}
</div>
</template>
<script setup>
import ChildMy from './child.vue'
import { ref } from 'vue'
const person = ref({
name: 'Andy',
age: 11
})
// 初始化定义时,当父组件没有传入默认值时候,子组件中的job值不父组件的值不同步,子组件展示的是子组件初始化的值--前端
const job = ref()
const num = ref(3)
</script>
子组件:
<template>
<div class="my-define-module">
child -- {{person.name}}
<hr>
--job--{{ job }}
<hr>
num---{{num}}
<el-button type="primary" @click="updatedName">child btn</el-button>
<el-button type="primary" @click="updatedJob">child job</el-button>
<el-button type="primary" @click="updatednum">child num</el-button>
</div>
</template>
<script setup>
import { ref, reactive } from 'vue'
// defineModel() 返回的是一个ref对象
const person = defineModel({
name: 'child',
age: 18,
})
const num = defineModel('num', 2)
const job = defineModel('job', '前端')
console.log('--job---', job) // value => 前端
console.log('--num---', num) // value => 3
console.log('==person==child=', person) // value => undefined
</script>
如图:
3、defineModel() 设置额外参数如类型、默认值
const job = defineModel('job', {default: '', type: String, required: true})
编译后的
props:{
job:{
default: '',
type: String,
required: true
}
}
4、defineModel() 添加自定义修饰符:
需要使用数组解构方法获取 model 和 modifiers
;model即为ref对象,modifiers即为修饰符对象;
如:
父组件:
<template>
<div class="my-define-module">
This is a defineModel text page.
<ChildMy v-model.upLow="job"/>
<hr>
parents--s{{ job }} // 初始化 5
</div>
</template>
<script setup>
import ChildMy from './child.vue'
import { ref } from 'vue'
const job = ref(5)
</script>
子组件:
<template>
<div class="my-define-module">
<hr>
--job--{{ model }}
<hr>
<el-button type="primary" @click="updatedJob">child job</el-button>
</div>
</template>
<script setup>
import { ref, reactive } from 'vue'
const [model, modifiers] = defineModel({
get(value) {
console.log('-get-job---', value)
return value
},
set(value) {
if (modifiers.upLow) { // 有upLow修饰符的v-model 会将值累加 22
return value + 22
}
return value
}
})
console.log('--job-model--', model)
console.log('--job-modifiers--', modifiers)
const updatedJob = () => {
model.value = model.value + 10 // 更新model.value的值
}
</script>