依赖注入Provide / Inject

通常,当我们需要从父组件向子组件传递数据时,我们使用 props。想象一下这样的结构:有一些深度嵌套的组件,而深层的子组件只需要父组件的部分内容。在这种情况下,如果仍然将 prop 沿着组件链逐级传递下去,可能会很麻烦。

官网的解释很让人疑惑,那我翻译下这几句话:

provide 可以在祖先组件中指定我们想要提供给后代组件的数据或方法,而在任何后代组件中,我们都可以使用 inject 来接收 provide 提供的数据或方法。

爷爷级组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
<!--
* @Description:
* @Author: xiuji
* @Date: 2022-08-31 21:14:30
* @LastEditTime: 2022-10-21 15:28:43
* @LastEditors: Do not edit
-->
<template>
<div class="content">
<h1>爷爷级</h1>
<label>
<input type="radio" v-model="colorVal" value="red" name="color">
红色
</label>
<label>
<input type="radio" v-model="colorVal" value="blue" name="color">
蓝色
</label>
<label>
<input type="radio" v-model="colorVal" value="pink" name="color">
粉色
</label>
<div class="box"></div>
<provideFather></provideFather>
</div>
</template>

<script setup lang="ts">
import { ref, provide, readonly } from 'vue'
import provideFather from './components/provideFather.vue'
const colorVal = ref<string>('red')
// 使用readonly阻止深层组件对响应式参数的修改
provide('color', readonly(colorVal)) // 向provide中注入值
</script>

<style lang="scss" scoped>
.content {
position: relative;
flex: 1;
height: 100%;
margin: 20px;
border: 1px solid #ccc;
overflow: auto;

.box {
width: 50px;
height: 50px;
background-color: v-bind(colorVal);
}
}
</style>

父级组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<!--
* @Description:
* @Author: xiuji
* @Date: 2022-10-11 16:10:03
* @LastEditTime: 2022-10-11 16:55:39
* @LastEditors: Do not edit
-->
<template>
<div class="provideFather">
<h1>父级</h1>
<div class="box"></div>
<provideSon></provideSon>
</div>
</template>

<script setup lang="ts">
import { inject, Ref } from 'vue'
import provideSon from './provideSon.vue'
const color = inject<Ref<string>>('color')

</script>

<style scoped lang="scss">
.provideFather {
.box {
width: 50px;
height: 50px;
background-color: v-bind(color);
}

}
</style>

孙子级组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<!--
* @Description:
* @Author: xiuji
* @Date: 2022-10-11 16:10:26
* @LastEditTime: 2022-10-21 16:44:58
* @LastEditors: Do not edit
-->
<template>
<div class="provideSon">
<h1>儿子</h1>
<div>
<button @click="change">change color</button>
</div>
<div class="box"></div>
</div>
</template>

<script setup lang="ts">
import { inject } from 'vue'
import type { Ref } from 'vue'
const color = inject<Ref<string>>('color')
// 深层次子组件中可以直接修改provide注入的值
const change = () => {
// color?.value = 'green' // 可选链操作符不能够被赋值
color!.value = 'green' // 非空类型断言一般都是使用!叹号表示。表示可以确定某个标识符是有值的,跳过ts在编译阶段对它的检测
}
</script>

<style scoped lang="scss">
.provideFather {
.box {
width: 50px;
height: 50px;
background-color: v-bind(color);
}

}
</style>

使用Provide、Inject可以简化深层组件之间参数传递的步骤