vue3探索——组件通信之依赖注入
阅读原文时间:2023年08月31日阅读:4

背景

通常情况下,当我们需要从父组件向子组件传递数据时,会使用 props。想象一下这样的结构:有一些多层级嵌套的组件,形成了一颗巨大的组件树,而某个深层的子组件需要一个较远的祖先组件中的部分数据。在这种情况下,如果仅使用 props 则必须将其沿着组件链逐级传递下去,这会非常麻烦:

对于这种组件使用结构,vue3给我们提供了另外一种传参方式,那就是依赖注入。一个父组件相对于其所有的后代组件,会作为依赖提供者(provide)。任何后代的组件树,无论层级有多深,都可以注入(inject)由父组件提供给整条链路的依赖。

基础使用

provide的使用非常简单,下面给出代码。

<script setup lang="ts">
// 1-引入
import { ref, provide } from 'vue';

const param = ref();

// 2-使用
provide('mark', param);
</script>

provide()函数需要传入两个参数:

  • 第一个参数被称为注入名,可以是一个字符串或是一个 Symbol。后代组件会用注入名来查找期望注入的值。一个组件可以多次调用 provide(),使用不同的注入名,注入不同的依赖值。
  • 第二个参数是提供的值,值可以是任意类型,包括响应式的状态,比如一个 ref。提供的响应式状态使后代组件可以由此和提供者建立响应式的联系。

增强功能:除了在一个组件中提供依赖,我们还可以在整个应用层面提供依赖,类似于vue2中挂载在vue实例的原型对象上

import { createApp } from 'vue'

const app = createApp({})

app.provide(/* 注入名 */ 'message', /* 值 */ 'hello!')

在应用级别提供的数据在该应用内的所有组件中都可以注入。这在你编写插件时会特别有用,因为插件一般都不会使用组件形式来提供值。

要注入上层组件提供的数据,需使用 inject() 函数:

<script setup lang="ts">
// 1-引入
import { inject } from 'vue';

// 2-使用
const param = inject('mark');
// 第二个参数可以设置注入**默认值**,防止祖先组件没有提供依赖而造成报错
const param2 = inject('mark2', '默认值');
</script>

注意,不使用<script setup> 的话,需要在setup()函数中使用provide()inject()

import { provide } from 'vue'

export default {
  setup() {
    provide(/* 注入名 */ 'message', /* 值 */ 'hello!')
  }
}


import { inject } from 'vue'

export default {
  setup() {
    const message = inject('message')
    return { message }
  }
}

实战

// src/App.vue
<template>
    <div class="dad">
        <!-- 1000元 -->
        <h1>我是父组件,我有{{ money }}元</h1>
        <!-- 使用子组件 -->
        <son />
    </div>
</template>

<script setup lang="ts">
// 引入子组件
import son from './components/son.vue';
// 引入vue API
import { ref, provide } from 'vue';

// 1-父组件响应式数据
const money = ref(1000);

// 向后面的组件提供依赖
provide('Pmoney', money);
</script>

<style lang="scss" scoped>
.dad {
    width: 600px;
    height: 600px;
    background-color: skyblue;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
}
</style>


// src/components/son.vue
<template>
    <div class="son">
        <h2>我是子组件</h2>
        <!-- 使用孙组件 -->
        <grandson />
    </div>
</template>

<script setup lang="ts">
// 引入孙组件
import grandson from './grandson.vue';
</script>

<style lang="scss" scoped>
.son {
    height: 400px;
    width: 400px;
    background-color: pink;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
}
</style>


// src/components/grandson.vue
<template>
    <div class="grandson">
        <h3>我是孙组件,我爷爷有{{ money }}元</h3>
    </div>
</template>

<script setup lang="ts">
// 引入 inject
import { inject } from 'vue';

// 2-注入祖先组件的依赖
const money = inject('Pmoney');
</script>

<style lang="scss" scoped>
.grandson {
    width: 200px;
    height: 200px;
    background-color: aqua;
}
</style>