组件通信——provide 和 inject 实现爷孙组件通信

provide 和 inject 实现爷孙组件通信

介绍

provide 和 inject 是 Vue.js 提供的一种在组件之间共享数据的机制,它允许在组件树中的任何地方注入依赖项。这对于跨越多个层级的组件间通信特别有用,因此无需手动将 prop 数据逐层传递下去。

  • provide:

    • 在一个组件中使用 provide 方法来定义要提供的数据或方法。

    • provide 方法返回一个对象,该对象包含了要提供的数据或方法。

  • inject:

    • 在另一个组件中使用 inject 方法来注入这些数据或方法。

    • inject 方法接收一个数组或对象,指明要注入的数据或方法名称。

实现原理

provide 方法

当你在组件中定义 provide 方法时,Vue.js 会执行以下步骤:

  • 创建 provide 对象:
javascript 复制代码
provide() {
 return {
   sharedData: 'Hello from App component!',
   updateData: this.updateData
 };
}
  • 附加到组件实例:Vue.js 会在组件实例上附加一个 _provided 属性,存储 provide 方法返回的对象。

inject 方法

当你在组件中定义 inject 方法时,Vue.js 会执行以下步骤:

  • 查找 provide 对象:Vue.js 会从当前组件及其祖先组件中查找 _provided 属性。

    如果找到,则将相应的数据或方法注入到当前组件中。

  • 注入到组件实例:注入的数据或方法会作为属性添加到当前组件实例上,可以通过 this 访问。

优点
  • 简化多层级组件通信:不需要逐层传递 props,可以方便地在组件树中的任意位置提供和注入数据。

  • 灵活性高:可以动态地提供和注入数据或方法,适用于多种场景。

  • 减少代码冗余:减少了逐层传递 props 的代码量,提高了代码的可读性和可维护性。

缺点
  • 调试困难:由于数据传递路径不明确,调试时可能比较困难,尤其是在大型项目中。

  • 组件关系模糊:组件之间的依赖关系变得不清晰,可能导致代码难以理解和维护。

  • 性能开销:大量使用 provide 和 inject 可能导致额外的性能开销,特别是在复杂的应用中。

  • 滥用问题:如果过度使用 provide 和 inject,可能会导致组件之间的耦合度过高,降低代码的可维护性。

vue2.x 使用

grandpa.vue 组件(爷)

  • 使用 provide 方法提供了 message, message2 字符串和一个 sendGradpaMessage 方法。

  • sendGradpaMessage 方法用于接收子组件传递的消息。

javascript 复制代码
// grandpa.vue

<template>
  <div class="grandpa">
    <h2>爷组件</h2>
    <parent />
  </div>
</template>

<script>
import parent from './parent.vue';

export default {
  name: 'grandpa',
  data() {
    return {

    }
  },
  provide() {
    return {
      message: 'This is some shared data1',
      message2: 'This is some shared data2',
      sendGradpaMessage: this.getSendMessage
    };
  },
  components: {
    parent
  },
  methods: {
    getSendMessage(message) {
      console.log('Message received:', message);
    }
  }
}
</script>

parent.vue 组件(父)

  • 使用 inject 方法注入了从爷组件提供的 message 参数。
javascript 复制代码
// parent.vue

<template>
  <div class="parent">
    <h2>父组件</h2>
    <span>{{ message }}</span>
    <son />
  </div>
</template>

<script>
import son from './son.vue';

export default {
  name: 'parent',
  data() {
    return {

    }
  },
  inject: ['message'],
  components: { son },
  methods: {
   
  }
}
</script>

son.vue 组件(子)

  • 使用 inject 方法注入了从爷组件提供的 message2 字符串和一个 sendGradpaMessage 方法。

  • 当点击按钮时,调用 sendGradpaMessage 方法将消息传递给祖先组件。

javascript 复制代码
// son.vue

<template>
  <div class="son">
    <h2>孙组件</h2>
    <span>{{ message2 }}</span>
    <el-button type="primary" @click="sendMessage">发送消息</el-button>
  </div>
</template>

<script>
export default {
  name: 'son',
  data() {
    return {

    }
  },
  inject: ['message2', 'sendGradpaMessage'],

  methods: {
    sendMessage() {
      const msg = 'Hello from son component!';
      this.sendGradpaMessage(msg);
    }
  }
}
</script>

上述示例中:

  • grandpa.vue 组件通过 provide 方法提供了 message, message2 两个参数和一个名为 sendGradpaMessage 的方法。

  • parent.vue 组件通过 inject 接收了 message 参数,并在模板中使用 message。

  • son.vue 组件通过 inject 接收了 message2 参数,并在模板中使用 message,同时通过点击按钮触发 sendGradpaMessage 方法将参数 msg 传给了 grandpa.vue 组件。

类型检查

inject 的配置项可以是一个数组或者一个对象。当使用对象形式时,可以指定更多的配置选项,比如类型检查、默认值等。

配置选项详解

  • from :指定 inject 要注入的数据的键名。如果没有指定,则默认为 inject 的键名。

  • default:如果没有从祖先组件中找到对应的数据,则使用这个默认值。这对于确保组件即使在缺少某些数据的情况下也能正常工作是非常有用的。

  • from 和 default 的组合:你可以同时指定 from 和 default,在这种情况下,如果 from 指定的数据不存在,则使用 default 中定义的值。

例如,你可以指定默认值和别名:

javascript 复制代码
inject: {
 // 定义别名为 message2 的数据,其来源为 message2
  message2: {   
    from: 'message2',
     // 如果没有提供则使用默认值
    default: 'Default value if not provided'
  },
  // 定义别名为 sendGradpaMessage 的方法
  sendGradpaMessage: {
    from: 'sendGradpaMessage',
    // 如果没有提供则使用一个空函数作为默认值
    default: () => {}
  }
}

vue3.x 使用

在 Vue 3.x 中,provide 和 inject 的使用方式有所变化,主要是因为引入了 Composition API。在 Composition API 中,provide 和 inject 的使用更加灵活,并且通常在 setup 函数中进行操作。

注意事项:

1. Composition API 中的使用:

  • 在 Vue 3.x 中,provide 和 inject 必须在 setup 函数中使用。

  • provide 通常用来向子组件提供数据或方法。

  • inject 用来从父组件或其他祖先组件获取数据或方法。

2. 响应式处理:

  • 当使用 provide 提供一个响应式对象时,Vue 3.x 会自动处理它的响应性。然而,如果提供的数据不是响应式的,那么注入的数据也不会是响应式的。

3. 类型安全:

  • 在 TypeScript 项目中,可以使用类型注解来确保 provide 和 inject 的类型安全。

4. 默认值:

  • 在 Vue 3.x 中,inject 可以接受一个默认值作为第二个参数,如果找不到对应的 provide 数据,则使用默认值。

5. 调试:

  • 使用 provide 和 inject 时,确保在开发过程中使用 Vue Devtools 来帮助跟踪数据流。
示例

grandpa.vue 组件(爷)

  • 使用 provide 方法提供了 message, message2 字符串和一个 sendGradpaMessage 方法。

  • sendGradpaMessage 方法用于接收子组件传递的消息。

javascript 复制代码
// grandpa.vue

<template>
  <div class="grandpa">
    <h2>爷组件</h2>
    <div>{{ num1 }}</div>

    <parent />
  </div>
</template>

<script setup lang="ts" name="grandpa">
import { provide, ref } from 'vue';

import parent from './parent.vue';

// 定义一个可变的值
const message = ref('Hello from grandpa');
const message2 = ref('Hello from Parent');

let num1 = ref(0);
// 使用 provide 将这个值暴露给子组件
provide('message', message);
provide('message2', message2);
provide('sendGradpaMessage', getSendMessage);

// 接受 son 组件传递的参数
function getSendMessage(num: number) {
  console.log('msg::: ', num);
  num1.value = num
}
</script>

parent.vue 组件(父)

  • 使用 inject 方法注入了从爷组件提供的 message2 参数。
javascript 复制代码
// parent.vue

<template>
  <div class="parent">
    <h2>父组件</h2>
    <span>{{ message2 }}</span>
    <son />
  </div>
</template>

<script setup lang="ts" name="parent">
import son from './son.vue';
import { inject, onMounted } from 'vue';


// 使用 inject 获取父组件提供的值
const message2 = inject('message2');
</script>

son.vue 组件(子)

  • 使用 inject 方法注入了从爷组件提供的 message2 字符串和一个 sendGradpaMessage 方法。

  • 当点击按钮时,调用 sendGradpaMessage 方法将消息传递给祖先组件。

javascript 复制代码
// son.vue

<template>
  <div class="son">
    <h2>孙组件</h2>
    <div>{{ message }}</div>
    <el-button type="primary" @click="sendMessage(111)">发送消息</el-button>
  </div>
</template>

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

import { inject, onMounted } from 'vue';

// 使用 inject 获取父组件提供的值
const message = inject('message', '默认值');

const sendMessage = inject('sendGradpaMessage', (params: number) => { });

</script>

上述示例中:

  • grandpa.vue 组件通过 provide 方法提供了 message, message2 两个参数和一个名为 sendGradpaMessage 的方法。

  • parent.vue 组件通过 inject 接收了 message 参数,并在模板中使用 message。

  • son.vue 组件通过 inject 接收了 message2 参数,并在模板中使用 message,同时通过点击按钮触发 sendGradpaMessage 方法将参数 msg 传给了 grandpa.vue 组件。

总结

provide 和 inject 是 Vue.js 中一种用于跨越多个层级组件间通信的机制,通过在组件中定义 provide 方法提供数据或方法,并在其他组件中使用 inject 方法注入这些数据或方法,从而简化了多层级组件间的通信。这种方式不仅减少了逐层传递 props 的代码量,提高了代码的可读性和可维护性,还支持动态提供和注入数据,适用于多种场景。然而,过度使用 provide 和 inject可能会导致组件之间的耦合度增加,影响代码的调试和维护。

相关推荐
子兮曰5 天前
jev-ultrafast 深度解析:7 秒订机票的浏览器 Agent 是如何炼成的
前端·后端·agent
子兮曰5 天前
Jev 爆发一周:7 秒 Agent 背后的 System One 生态与三场争议
前端·后端·ai编程
前端小万5 天前
写公众号赚了 3000 块后,我做了一款叫 "一键成稿" 的软件
前端·微信小程序
爱勇宝5 天前
ZCode 开源 24 小时:一份没有历史的账本,回答不了"有没有偷代码"
前端·后端·chatglm (智谱)
A黄俊辉A5 天前
uniapp webview中实现 app和内嵌的H5双向通信
vue.js·json
三十而立洋5 天前
Cookie 详解:从产生到安全,一次讲透
前端·javascript
卡布鲁5 天前
把一个 Vite + Vue3 应用塞进 qiankun (React + Umi3) 主站:十个坑的复盘
前端·javascript·react.js
李少兄5 天前
JavaScript 隐式全局变量解析
javascript
honkun65 天前
vue 表格组件 vxe-table 配置 ajax 请求自动加载数据与表单查询
vue.js·vxe-table
kybs19915 天前
全球灾害数据分析可视化 毕业设计-附源码66794
vue.js·spring boot·mysql·安全·django·c#·asp.net