为什么会需要使用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 会将值累加 22return value + 22 }return value}
})
console.log('--job-model--', model)
console.log('--job-modifiers--', modifiers)
const updatedJob = () => {model.value = model.value + 10 // 更新model.value的值
}
</script>