一、为什么用他们?
provide/inject 主要用于父组件和子孙组件间通讯,不用在父传子,子传孙,孙传重孙等数据传递了(解决了Prop 逐级透传问题)。简单的父子组件间传值还是使用props、emits或是defineProps、defineEmits、defineExpose比较方便。
二、原理 与 区别
父组件中提供数据,并在其后代的组件树,无论层级有多深,都可以中注入这些数据,从而实现了组件之间的数据传递。。
1. 在vue2中,组件实例方法和属性的继承是通过原型链来实现。
- 当一个组件通过 provide 提供数据,它会将这些数据添加到其原型链上,然后子组件通过 inject 可以在原型链上查找并访问这些数据。
- vue 会遍历父组件链,通过匹配provide( key,value)和inject(key,default [可选])的 key 来确定所提供的值。如果父组件链上多个组件对同一个 key 提供了值,那么离得更近的组件将会“覆盖”链上更远的组件所提供的值。如果没有能通过 key 匹配到值,inject() 将返回 undefined,除非提供了一个默认值。
2. 在 Vue 3 中 组件不再依赖于原型链,而是 引入了 Composition API,直接导出给组件实例。
provide
是在父组件中使用的选项,它是一个函数,会在父组件实例上创建一个名为_provided
的对象。_provided
对象存储了提供给子组件的数据,而且这些数据会在整个组件树中可用,子组件可以通过inject
选项来访问这些数据。- 当子组件访问通过inject注入的数据时,Vue 3会在组件树中向上搜索父组件,一旦找到包含提供数据的组件,Vue 3会从该组件的_provided属性中获取相应的数据。
- 如果提供的数据是响应式的,子组件将自动成为这些数据的依赖,当提供的数据发生变化时,子组件将被通知并进行更新。
三、使用方式
1. vue2中的使用
父组件:
// 1. 对象形式
export default{
provide:{
info:"哈哈哈"
}
}
//2. provide 需要使用 data 内的数据(访问组件实例 property)时,需要将 provide 转换为返回对象的函数。
export default{
data() {
return {
msg: "哈哈哈"
};
},
provide() {
return {
info: this.msg //提供祖先组件的实例
};
},
}
后代组件:
// 1.
export default{
inject:['info'],
mounted(){
console.log("接收数据:", this.info) // 接收数据:哈哈哈
}
}
//2. 或者 对象形式
<template>
<div>
<P>姓名:{{info.msg}}</P>
</div>
</template>
<script>
export default {
inject: {
info: {
from: 'info', // 当声明注入的默认值时,必须使用对象形式,与原注入名同名时,这个属性可选
default: 'default value'
}
}
};
</script>
2. vue3中的使用
父组件:
//1.
<script>
import { provide } from "vue"
export default {
setup(){
provide('info',"哈哈哈");
provide('changeSubmitLoading', (val) => {
submitLoading.value = val
})
}
}
</script>
// 2. 添加响应性
<script setup>
import { provide, ref } from 'vue'
const location = ref('哈哈哈')
function updateLocation() {
location.value = '吼吼吼'
}
provide('location', {
location,
updateLocation
})
</script>
后代组件:
// 1.
<template>
{{info}}
</template>
<template #cell="{ record }">
<a-button size="mini" type="text" @click="handleDelete(record)">
</a-button>
</template>
<script>
import { inject } from "vue"
export default {
setup(){
const info = inject('info')
const changeSubmitLoading = inject('changeSubmitLoading')
const handleDelete = (record) => {
/* 方法内其他内容*/
changeSubmitLoading(true)
}
return{
info
}
}
}
</script>
//2. 添加响应性后
<template>
<button @click="updateLocation">{{ location }}</button>
</template>
<script setup>
import { inject } from 'vue'
const { location, updateLocation } = inject('location')
</script>