Vue3中的常见组件通信之`provide`、`inject`

Vue3中的常见组件通信之provideinject

概述

​ 在vue3中常见的组件通信有props、mitt、v-model、 r e f s 、 refs、 refsparent、provide、inject、pinia、slot等。不同的组件关系用不同的传递方式。常见的撘配形式如下表所示。

组件关系传递方式
父传子1. props
2. v-model
3. $refs
4. 默认插槽、具名插槽
子传父1. props
2. 自定义事件
3. v-model
4. $parent
5. 作用域插槽
祖传孙、孙传祖1. $attrs
2. provide、inject
兄弟间、任意组件间1. mitt
2. pinia

props和自定义事件详见:
Vue3中的常见组件通信之props和自定义事件

mitt用法详见:
Vue3中的常见组件通信之mitt

v-model用法详见:
Vue3中的常见组件通信之v-model

$attrs用法详见:
Vue3中的常见组件通信之$attrs

$refs$parent详见:
Vue3中的常见组件通信之$refs$parent

接下来是provide和inject。

7.provide和inject

provideinject用于当前组件向其后代组件直接通信,需要先在祖先组件中通过provide配置向后代组件提供数据,然后在后代组件中通过inject配置声明接收数据。

7.1准备三个组件

先准备三个组件,分别为父组件、子组件和孙组件,具体代码如下:

父组件代码:

<template>
  <div class="father">
    <h3>父组件</h3>
    <h4>用户订单详情</h4>
    <ul>
      <li>订单编号:{{ oderDetail.id }}</li>
      <li>订单用户:{{ oderDetail.username }}</li>
      <li>订单商品:{{ oderDetail.goods }}</li>
      <li>订单价格:{{ oderDetail.price }}</li>
    </ul>
    <Child/>
  </div>
</template>

<script lang="ts" setup name="Father">
import Child from './Child.vue'
import {reactive} from 'vue'

let oderDetail = reactive({
  id:"abc01",
  username:'xiaopeng',
  goods:"神仙水",
  price:998
})
</script>

<style scoped>
  .father{
    background-color: rgb(112, 150, 66);
    margin: 10px;
    padding: 10px;
    border-radius: 5px;
  }
</style>

子组件代码:

<template>
  <div class="child">
    <h3>子组件</h3>
    <GrandChild/>
  </div>
</template>

<script lang="ts" setup name="Child">
  import GrandChild from './GrandChild.vue'

</script>

<style scoped>
  .child{
    background-color: burlywood;
    margin: 10px;
    padding: 10px;
    border-radius: 10px;
  }
</style>

孙组件代码:

<template>
  <div class="grandChild">
    <h3>
      这是孙组件
    </h3>
  </div>
</template>

<script lang="ts" setup name="GrandChild">

</script>

<style scoped>
  .grandChild{
    background-color: aqua;
    margin: 10px;
    padding:10px;
    border-radius: 5px;
  }
</style>

运行效果如下:

image-20240609172455063

7.2 祖传孙通信的实现

在父组件中引入provide,并提供数据

import {reactive,provide} from 'vue'
//提供数据
provide('oderDetail',oderDetail)

注意此处第一个参数的名字可以是任意的,第二个参数为数据,如果是多个数据,可以为对象格式的数据。

在后代组件中(以孙组件为例)引入inject,并注入数据:

import { inject } from 'vue';

//注入数据
let oderDetail = inject('oderDetail')

注意inject中的第一个参数必须与祖组件中provide中第一个参数相同,第二个参数为默认值,即当父组件中没有传递'oderDetail',那么孙组件中的oderDetail的值就是procide中的第二个参数。

孙组件中在页面中呈现:

<h4>父组件传递过来的用户订单详情</h4>
    <ul>
      <li>订单编号:{{ oderDetail.id }}</li>
      <li>订单用户:{{ oderDetail.username }}</li>
      <li>订单商品:{{ oderDetail.goods }}</li>
      <li>订单价格:{{ oderDetail.price }}</li>
    </ul>

注意上面代码中VS Code进行TS检查,会提示oderDetail有错误,但是实际不影响页面呈现,运行结果如下:

image-20240609233257739

接下来需要处理ts的问题,ts显示oderDetail类型为未知,那么可以在定义oderDetail的时候给个默认值,如下代码:

let oderDetail = inject('oderDetail',{id:'',username:'',goods:'',price:0})

此时VS Code就不再报错,并且运行结果一样。

7.3 孙传祖通信的实现

provide和inject也可以实现孙传祖通信,需要在父组件中定义一个函数,并传递给后代:

//方法
function discount(value:number){
  oderDetail.price = oderDetail.price * value/10
}
//提供方法
provide('discount',discount)

孙组件中接收方法:

let discount = inject('discount',(value:number)=>{})

孙组件中添加按钮并绑定单击事件触发接收的方法,并传递参数:

<button @click="discount(7)">父组件中的订单价格打7折</button>

运行后单击按钮可以实现更改父组件中的价格,由于孙组件中接收的数据为相应式的,因此更改父组件中的价格,孙组件中的订单价格也会相应变化,如下图所示:

至此以及实现了孙传祖通信。

不过上面代码还可以简化,之前提到过provide第二个参数如果是多个数据,可以为对象格式的数据,这样就可以把数据和对象同时传递和接收,如下代码所示:

//提供数据和方法
provide('oderContent',{oderDetail,discount})
let {oderDetail,discount} = inject('oderContent',{oderDetail:{id:'',username:'',goods:'',price:0},discount:(value:number)=>{}})

这样运行的结果是完全一样的。

7.4 小结

provideinject用于当前组件向其后代组件直接通信,需要先在祖先组件中通过provide配置向后代组件提供数据,然后在后代组件中通过inject配置声明接收数据。这个过程是完全不打扰中间的子组件,实现的是祖孙间的直接通信。

下面是完整代码:

父组件:

<template>
  <div class="father">
    <h3>父组件</h3>
    <h4>用户订单详情</h4>
    <ul>
      <li>订单编号:{{ oderDetail.id }}</li>
      <li>订单用户:{{ oderDetail.username }}</li>
      <li>订单商品:{{ oderDetail.goods }}</li>
      <li>订单价格:{{ oderDetail.price }}</li>
    </ul>
    <Child/>
  </div>
</template>

<script lang="ts" setup name="Father">
import Child from './Child.vue'
import {reactive,provide} from 'vue'

//数据
let oderDetail = reactive({
  id:"abc01",
  username:'xiaopeng',
  goods:"神仙水",
  price:998
})

//方法
function discount(value:number){
  oderDetail.price = oderDetail.price * value/10
}
// //提供数据
// provide('oderDetail',oderDetail)
// 提供方法
// provide('discount',discount)

//提供数据和方法
provide('oderContent',{oderDetail,discount})

</script>

<style scoped>
  .father{
    background-color: rgb(112, 150, 66);
    margin: 10px;
    padding: 10px;
    border-radius: 5px;
  }
</style>

子组件:

<template>
  <div class="child">
    <h3>子组件</h3>
    <GrandChild/>
  </div>
</template>

<script lang="ts" setup name="Child">
  import GrandChild from './GrandChild.vue'

</script>

<style scoped>
  .child{
    background-color: burlywood;
    margin: 10px;
    padding: 10px;
    border-radius: 10px;
  }
</style>

孙组件:

<template>
  <div class="grandChild">
    <h3>
      这是孙组件
    </h3>
    <h4>父组件传递过来的用户订单详情</h4>
    <ul>
      <li>订单编号:{{ oderDetail.id }}</li>
      <li>订单用户:{{ oderDetail.username }}</li>
      <li>订单商品:{{ oderDetail.goods }}</li>
      <li>订单价格:{{ oderDetail.price }}</li>
    </ul>
    <button @click="discount(7)">父组件中的订单价格打7折</button>
  </div>
</template>

<script lang="ts" setup name="GrandChild">
import { inject } from 'vue';

// // 注入数据
// let oderDetail = inject('oderDetail',{id:'',username:'',goods:'',price:0})
// let discount = inject('discount',(value:number)=>{})

let {oderDetail,discount} = inject('oderContent',{oderDetail:{id:'',username:'',goods:'',price:0},discount:(value:number)=>{}})

</script>

<style scoped>
  .grandChild{
    background-color: aqua;
    margin: 10px;
    padding:10px;
    border-radius: 5px;
  }
</style>
  • 14
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值