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

Vue3中的常见组件通信之provideinject

概述

​ 在vue3中常见的组件通信有props、mitt、v-model、 r e f s 、 refs、 refs、parent、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准备三个组件

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

父组件代码:

vue 复制代码
<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>

子组件代码:

vue 复制代码
<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>

孙组件代码:

vue 复制代码
<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>

运行效果如下:

7.2 祖传孙通信的实现

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

typescript 复制代码
import {reactive,provide} from 'vue'
typescript 复制代码
//提供数据
provide('oderDetail',oderDetail)

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

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

typescript 复制代码
import { inject } from 'vue';

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

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

孙组件中在页面中呈现:

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

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

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

typescript 复制代码
let oderDetail = inject('oderDetail',{id:'',username:'',goods:'',price:0})

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

7.3 孙传祖通信的实现

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

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

孙组件中接收方法:

typescript 复制代码
let discount = inject('discount',(value:number)=>{})

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

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

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

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

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

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

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

7.4 小结

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

下面是完整代码:

父组件:

vue 复制代码
<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>

子组件:

vue 复制代码
<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>

孙组件:

vue 复制代码
<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>
相关推荐
别拿曾经看以后~18 分钟前
【el-form】记一例好用的el-input输入框回车调接口和el-button按钮防重点击
javascript·vue.js·elementui
我要洋人死21 分钟前
导航栏及下拉菜单的实现
前端·css·css3
川石课堂软件测试23 分钟前
性能测试|docker容器下搭建JMeter+Grafana+Influxdb监控可视化平台
运维·javascript·深度学习·jmeter·docker·容器·grafana
科技探秘人32 分钟前
Chrome与火狐哪个浏览器的隐私追踪功能更好
前端·chrome
科技探秘人33 分钟前
Chrome与傲游浏览器性能与功能的深度对比
前端·chrome
JerryXZR38 分钟前
前端开发中ES6的技术细节二
前端·javascript·es6
七星静香40 分钟前
laravel chunkById 分块查询 使用时的问题
java·前端·laravel
q24985969343 分钟前
前端预览word、excel、ppt
前端·word·excel
小华同学ai1 小时前
wflow-web:开源啦 ,高仿钉钉、飞书、企业微信的审批流程设计器,轻松打造属于你的工作流设计器
前端·钉钉·飞书
problc1 小时前
Flutter中文字体设置指南:打造个性化的应用体验
android·javascript·flutter