provide 和 inject 实现爷孙组件通信
介绍
provide
和 inject
是 Vue.js 提供的一种在组件之间共享数据的机制,它允许在组件树中的任何地方注入依赖项。这对于跨越多个层级的组件间通信特别有用,因此无需手动将 prop
数据逐层传递下去。
-
provide:
-
在一个组件中使用
provide
方法来定义要提供的数据或方法。 -
provide
方法返回一个对象,该对象包含了要提供的数据或方法。
-
-
inject:
-
在另一个组件中使用
inject
方法来注入这些数据或方法。 -
inject
方法接收一个数组或对象,指明要注入的数据或方法名称。
-
实现原理
provide 方法
当你在组件中定义 provide
方法时,Vue.js 会执行以下步骤:
- 创建
provide
对象:
javascript
provide() {
return {
sharedData: 'Hello from App component!',
updateData: this.updateData
};
}
- 附加到组件实例:Vue.js 会在组件实例上附加一个
_provided
属性,存储provide
方法返回的对象。
inject 方法
当你在组件中定义 inject
方法时,Vue.js 会执行以下步骤:
-
查找
provide
对象:Vue.js 会从当前组件及其祖先组件中查找_provided
属性。如果找到,则将相应的数据或方法注入到当前组件中。
-
注入到组件实例:注入的数据或方法会作为属性添加到当前组件实例上,可以通过
this
访问。
优点
-
简化多层级组件通信:不需要逐层传递
props
,可以方便地在组件树中的任意位置提供和注入数据。 -
灵活性高:可以动态地提供和注入数据或方法,适用于多种场景。
-
减少代码冗余:减少了逐层传递 props 的代码量,提高了代码的可读性和可维护性。
缺点
-
调试困难:由于数据传递路径不明确,调试时可能比较困难,尤其是在大型项目中。
-
组件关系模糊:组件之间的依赖关系变得不清晰,可能导致代码难以理解和维护。
-
性能开销:大量使用
provide
和inject
可能导致额外的性能开销,特别是在复杂的应用中。 -
滥用问题:如果过度使用
provide
和inject
,可能会导致组件之间的耦合度过高,降低代码的可维护性。
vue2.x 使用
grandpa.vue 组件(爷)
-
使用
provide
方法提供了message
,message2
字符串和一个sendGradpaMessage
方法。 -
sendGradpaMessage 方法用于接收子组件传递的消息。
javascript
// grandpa.vue
<template>
<div class="grandpa">
<h2>爷组件</h2>
<parent />
</div>
</template>
<script>
import parent from './parent.vue';
export default {
name: 'grandpa',
data() {
return {
}
},
provide() {
return {
message: 'This is some shared data1',
message2: 'This is some shared data2',
sendGradpaMessage: this.getSendMessage
};
},
components: {
parent
},
methods: {
getSendMessage(message) {
console.log('Message received:', message);
}
}
}
</script>
parent.vue 组件(父)
- 使用
inject
方法注入了从爷组件提供的message
参数。
javascript
// parent.vue
<template>
<div class="parent">
<h2>父组件</h2>
<span>{{ message }}</span>
<son />
</div>
</template>
<script>
import son from './son.vue';
export default {
name: 'parent',
data() {
return {
}
},
inject: ['message'],
components: { son },
methods: {
}
}
</script>
son.vue 组件(子)
-
使用
inject
方法注入了从爷组件提供的message2
字符串和一个sendGradpaMessage
方法。 -
当点击按钮时,调用
sendGradpaMessage
方法将消息传递给祖先组件。
javascript
// son.vue
<template>
<div class="son">
<h2>孙组件</h2>
<span>{{ message2 }}</span>
<el-button type="primary" @click="sendMessage">发送消息</el-button>
</div>
</template>
<script>
export default {
name: 'son',
data() {
return {
}
},
inject: ['message2', 'sendGradpaMessage'],
methods: {
sendMessage() {
const msg = 'Hello from son component!';
this.sendGradpaMessage(msg);
}
}
}
</script>
上述示例中:
-
grandpa.vue 组件通过
provide
方法提供了message
,message2
两个参数和一个名为sendGradpaMessage
的方法。 -
parent.vue 组件通过
inject
接收了message
参数,并在模板中使用message
。 -
son.vue 组件通过
inject
接收了message2
参数,并在模板中使用message
,同时通过点击按钮触发sendGradpaMessage
方法将参数msg
传给了 grandpa.vue 组件。
类型检查
inject
的配置项可以是一个数组或者一个对象。当使用对象形式时,可以指定更多的配置选项,比如类型检查、默认值等。
配置选项详解
-
from
:指定inject
要注入的数据的键名。如果没有指定,则默认为inject
的键名。 -
default
:如果没有从祖先组件中找到对应的数据,则使用这个默认值。这对于确保组件即使在缺少某些数据的情况下也能正常工作是非常有用的。 -
from
和default
的组合:你可以同时指定 from 和 default,在这种情况下,如果 from 指定的数据不存在,则使用 default 中定义的值。
例如,你可以指定默认值和别名:
javascript
inject: {
// 定义别名为 message2 的数据,其来源为 message2
message2: {
from: 'message2',
// 如果没有提供则使用默认值
default: 'Default value if not provided'
},
// 定义别名为 sendGradpaMessage 的方法
sendGradpaMessage: {
from: 'sendGradpaMessage',
// 如果没有提供则使用一个空函数作为默认值
default: () => {}
}
}
vue3.x 使用
在 Vue 3.x 中,provide
和 inject
的使用方式有所变化,主要是因为引入了 Composition API。在 Composition API 中,provide
和 inject
的使用更加灵活,并且通常在 setup
函数中进行操作。
注意事项:
1. Composition API 中的使用:
-
在 Vue 3.x 中,provide 和 inject 必须在 setup 函数中使用。
-
provide 通常用来向子组件提供数据或方法。
-
inject 用来从父组件或其他祖先组件获取数据或方法。
2. 响应式处理:
- 当使用 provide 提供一个响应式对象时,Vue 3.x 会自动处理它的响应性。然而,如果提供的数据不是响应式的,那么注入的数据也不会是响应式的。
3. 类型安全:
- 在 TypeScript 项目中,可以使用类型注解来确保 provide 和 inject 的类型安全。
4. 默认值:
- 在 Vue 3.x 中,inject 可以接受一个默认值作为第二个参数,如果找不到对应的 provide 数据,则使用默认值。
5. 调试:
- 使用 provide 和 inject 时,确保在开发过程中使用 Vue Devtools 来帮助跟踪数据流。
示例
grandpa.vue 组件(爷)
-
使用
provide
方法提供了message
,message2
字符串和一个sendGradpaMessage
方法。 -
sendGradpaMessage 方法用于接收子组件传递的消息。
javascript
// grandpa.vue
<template>
<div class="grandpa">
<h2>爷组件</h2>
<div>{{ num1 }}</div>
<parent />
</div>
</template>
<script setup lang="ts" name="grandpa">
import { provide, ref } from 'vue';
import parent from './parent.vue';
// 定义一个可变的值
const message = ref('Hello from grandpa');
const message2 = ref('Hello from Parent');
let num1 = ref(0);
// 使用 provide 将这个值暴露给子组件
provide('message', message);
provide('message2', message2);
provide('sendGradpaMessage', getSendMessage);
// 接受 son 组件传递的参数
function getSendMessage(num: number) {
console.log('msg::: ', num);
num1.value = num
}
</script>
parent.vue 组件(父)
- 使用
inject
方法注入了从爷组件提供的message2
参数。
javascript
// parent.vue
<template>
<div class="parent">
<h2>父组件</h2>
<span>{{ message2 }}</span>
<son />
</div>
</template>
<script setup lang="ts" name="parent">
import son from './son.vue';
import { inject, onMounted } from 'vue';
// 使用 inject 获取父组件提供的值
const message2 = inject('message2');
</script>
son.vue 组件(子)
-
使用
inject
方法注入了从爷组件提供的message2
字符串和一个sendGradpaMessage
方法。 -
当点击按钮时,调用
sendGradpaMessage
方法将消息传递给祖先组件。
javascript
// son.vue
<template>
<div class="son">
<h2>孙组件</h2>
<div>{{ message }}</div>
<el-button type="primary" @click="sendMessage(111)">发送消息</el-button>
</div>
</template>
<script setup lang="ts" name="son">
import { inject, onMounted } from 'vue';
// 使用 inject 获取父组件提供的值
const message = inject('message', '默认值');
const sendMessage = inject('sendGradpaMessage', (params: number) => { });
</script>
上述示例中:
-
grandpa.vue 组件通过
provide
方法提供了message
,message2
两个参数和一个名为sendGradpaMessage
的方法。 -
parent.vue 组件通过
inject
接收了message
参数,并在模板中使用message
。 -
son.vue 组件通过
inject
接收了message2
参数,并在模板中使用message
,同时通过点击按钮触发sendGradpaMessage
方法将参数msg
传给了 grandpa.vue 组件。
总结
provide
和 inject
是 Vue.js 中一种用于跨越多个层级组件间通信的机制,通过在组件中定义 provide
方法提供数据或方法,并在其他组件中使用 inject
方法注入这些数据或方法,从而简化了多层级组件间的通信。这种方式不仅减少了逐层传递 props
的代码量,提高了代码的可读性和可维护性,还支持动态提供和注入数据,适用于多种场景。然而,过度使用 provide
和 inject
可能会导致组件之间的耦合度增加,影响代码的调试和维护。