一、为什么用他们?
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>