依赖注入 — Provide/Inject
1.关于
对于前面所说的组件通信方式(props
、emits
、refs
),当只有父子两级时,使用起来较为方便,但在实际的开发中,组件之间的嵌套关系往往并不止于父子两级,所以如果还选择使用前面的方式,例如props
,就会导致组件的模板字面量非常的长,同时,也可能会出现各种难以预期的情况。
而这里要说的Provide
和Inject
就是用于解决这一问题的。
2.示例
这里用三个组件来进行演示
父级组件 parent:
<template>
<section class="item" @click="onClick">PARENT {{ num }}</section>
<childVue /> <!-- 引入子组件 -->
</template>
<script setup>
import childVue from './child.vue';
import { ref, provide } from 'vue';
const num = ref(0);
const onClick = () => {
num.value++;
};
provide('count', { num });
</script>
子组件 child:
<template>
<section class="item">CHILD</section>
<grandchildren /><!-- 引入孙组件 -->
</template>
孙组件 grandchild:
<template>
<section class="item">GRANDCHILDREN {{num}}</section>
<grandchildren />
</template>
<script setup>
import { inject } from 'vue';
const { num } = inject('count');
</script>
此时页面的显示效果为:
PARENT 0
CHILD
GRANDCHILDREN 0
即在孙组件中可以使用由父组件传递过来的数据。
3.用法
3.1 语法解释
provide()
方法,接收两个参数:
- 第一个参数是要注入的
key
,可以是一个字符串或者一个Symbol
; - 第二个参数则是要注入的值
inject()
方法,可以接收三个参数:
- 第一个参数对应注入依赖的
key
。 - 第二个参数是可选的,即在没有匹配到
key
时使用的默认值。它可以是一个工厂函数,用于返回某些创建起来比较复杂的值。 - 第三个参数是可选的,其类型为
Boolean
。当第二个参数中的默认值本身就是一个函数,但不是工厂函数时,需要将该参数置为false
。
3.2 配合响应式使用
当注入的值为响应式数据时,建议尽可能将任何对响应式状态的变更都保持在provide
内部。
这样的话,可以确保对该响应式数据的状态和变更操作,都在同一个组件中,使其更容易维护。
还是用前面的例子,如果需要实现在孙组件中修改num
的值,则在父组件中注入时应当提供对应的修改方法:
<template>
<section class="item" @click="onClick">PARENT {{ num }}</section>
<childVue /> <!-- 引入子组件 -->
</template>
<script setup>
import childVue from './child.vue';
import { ref, provide } from 'vue';
const num = ref(0);
const onClick = () => {
num.value++;
};
// 修改num状态的函数
const increaseNum = (params) => {
num.value += params;
};
// 将 increaseNum 函数作为值提供
provide('count', { num, increaseNum });
</script>
这样,在孙组件中,就可以通过提供的方法触发父组件中的函数,对值进行修改:
<template>
<!-- 新增点击事件 -->
<section class="item" @click="onClick">GRANDCHILDREN</section>
<grandchildren />
</template>
<script setup>
import { inject } from 'vue';
// 在注入出添加 increaseNum 变量
const { num, increaseNum } = inject('count');
// 新增的点击事件,触发 increaseNum 并且传参为 2
const onClick = () => {
increaseNum(2);
};
</script>