Vue3全局组件通信之provide / inject详解

目录
  • 1、前言
  • 2、provide / inject
  • 3、发起 provide
  • 4、接收 inject
  • 5、响应性数据的传递与接收
  • 6、引用类型的传递与接收 (针对非响应性数据的处理)
  • 7、基本类型的传递与接收 (针对非响应性数据的处理)

1、前言

顾名思义,爷孙组件是比 父子组件通信 要更深层次的引用关系(也有称之为 “隔代组件”):

C组件引入到B组件里,B组件引入到A组件里渲染,此时A是C的爷爷级别(可能还有更多层级关系),如果你用 props ,只能一级一级传递下去,那就太繁琐了,因此我们需要更直接的通信方式。

他们之间的关系如下,Grandson.vue 并非直接挂载在 Grandfather.vue 下面,他们之间还隔着至少一个 Son.vue (可能有多个):

Grandfather.vue
└─Son.vue
  └─Grandson.vue

因为上下级的关系的一致性,爷孙组件通信的方案也适用于 父子组件通信 ,只需要把爷孙关系换成父子关系即可。

2、provide / inject

这个特性有两个部分:Grandfather.vue 有一个 provide 选项来提供数据,Grandson.vue 有一个 inject 选项来开始使用这些数据。

  • Grandfather.vue 通过 provide 向 Grandson.vue 传值(可包含定义好的函数)
  • Grandson.vue 通过 inject 向 Grandfather.vue 触发爷爷组件的事件执行

无论组件层次结构有多深,发起 provide 的组件都可以作为其所有下级组件的依赖提供者
这一部分的内容变化都特别大,但使用起来其实也很简单,不用慌,也有相同的地方:

  • 父组件不需要知道哪些子组件使用它 provide 的 property
  • 子组件不需要知道 inject property 来自哪里

另外,要切记一点就是:provide 和 inject 绑定并不是可响应的。这是刻意为之的,但如果传入了一个可监听的对象,那么其对象的 property 还是可响应的。

3、发起 provide

我们先来回顾一下 2.x 的用法:

export default {
  // 定义好数据
  data () {
    return {
      tags: [ '中餐', '粤菜', '烧腊' ]
    }
  },
  // provide出去
  provide () {
    return {
      tags: this.tags
    }
  }
}

旧版的 provide 用法和 data 类似,都是配置为一个返回对象的函数。
3.x 的新版 provide, 和 2.x 的用法区别比较大。

在 3.x , provide 需要导入并在 setup 里启用,并且现在是一个全新的方法。
每次要 provide 一个数据的时候,就要单独调用一次。
每次调用的时候,都需要传入 2 个参数:

参数 类型 说明
key string 数据的名称
value any 数据的值

来看一下如何创建一个 provide:

// 记得导入provide
import { defineComponent, provide } from 'vue'

export default defineComponent({
  // ...
  setup () {
    // 定义好数据
    const msg: string = 'Hello World!';

    // provide出去
    provide('msg', msg);
  }
})

操作非常简单对吧,但需要注意的是,provide 不是响应式的,如果你要使其具备响应性,你需要传入响应式数据

4、接收 inject

也是先来回顾一下 2.x 的用法:

export default {
  inject: ['tags'],
  mounted () {
    console.log(this.tags);
  }
}

旧版的 inject 用法和 props 类似,3.x 的新版 inject, 和 2.x 的用法区别也是比较大。

在 3.x, inject 和 provide 一样,也是需要先导入然后在 setup 里启用,也是一个全新的方法。
每次要 inject 一个数据的时候,就要单独调用一次。
每次调用的时候,只需要传入 1 个参数:

参数 类型 说明
key string 与provide对应的数据名称
// 记得导入inject
import { defineComponent, inject } from 'vue'

export default defineComponent({
  // ...
  setup () {
    const msg: string = inject('msg') || '';
  }
})

也是很简单(写 TS 的话,由于 inject 到的值可能是 undefined,所以要么加个 undefined 类型,要么给变量设置一个空的默认值)。

5、响应性数据的传递与接收

之所以要单独拿出来说, 是因为变化真的很大

在前面我们已经知道,provide 和 inject 本身不可响应,但是并非完全不能够拿到响应的结果,只需要我们传入的数据具备响应性,它依然能够提供响应支持。

我们以 ref 和 reactive 为例,来看看应该怎么发起 provide 和接收 inject。

先在 Grandfather.vue 里 provide 数据:

export default defineComponent({
  // ...
  setup () {
    // provide一个ref
    const msg = ref<string>('Hello World!');
    provide('msg', msg);

    // provide一个reactive
    const userInfo: Member = reactive({
      id: 1,
      name: 'Petter'
    });
    provide('userInfo', userInfo);

    // 2s 后更新数据
    setTimeout(() => {
      // 修改消息内容
      msg.value = 'Hi World!';

      // 修改用户名
      userInfo.name = 'Tom';
    }, 2000);
  }
})

在 Grandsun.vue 里 inject 拿到数据:

export default defineComponent({
  setup () {
    // 获取数据
    const msg = inject('msg');
    const userInfo = inject('userInfo');

    // 打印刚刚拿到的数据
    console.log(msg);
    console.log(userInfo);

    // 因为 2s 后数据会变,我们 3s 后再看下,可以争取拿到新的数据
    setTimeout(() => {
      console.log(msg);
      console.log(userInfo);
    }, 3000);

    // 响应式数据还可以直接给 template 使用,会实时更新
    return {
      msg,
      userInfo
    }
  }
})

非常简单,非常方便!!!

响应式的数据 provide 出去,在子孙组件拿到的也是响应式的,并且可以如同自身定义的响应式变量一样,直接 return 给 template 使用,一旦数据有变化,视图也会立即更新。

但上面这句话有效的前提是,不破坏数据的响应性,比如 ref 变量,你需要完整的传入,而不能只传入它的 value,对于 reactive 也是同理,不能直接解构去破坏原本的响应性

切记!切记!!!

6、引用类型的传递与接收 (针对非响应性数据的处理)

provide 和 inject 并不是可响应的,这是官方的故意设计,但是由于引用类型的特殊性,在子孙组件拿到了数据之后,他们的属性还是可以正常的响应变化。

先在 Grandfather.vue 里 provide 数据:

export default defineComponent({
  // ...
  setup () {
    // provide 一个数组
    const tags: string[] = [ '中餐', '粤菜', '烧腊' ];
    provide('tags', tags);

    // provide 一个对象
    const userInfo: Member = {
      id: 1,
      name: 'Petter'
    };
    provide('userInfo', userInfo);

    // 2s 后更新数据
    setTimeout(() => {
      // 增加tags的长度
      tags.push('叉烧');

      // 修改userInfo的属性值
      userInfo.name = 'Tom';
    }, 2000);
  }
})

在 Grandsun.vue 里 inject 拿到数据:

export default defineComponent({
  setup () {
    // 获取数据
    const tags: string[] = inject('tags') || [];
    const userInfo: Member = inject('userInfo') || {
      id: 0,
      name: ''
    };

    // 打印刚刚拿到的数据
    console.log(tags);
    console.log(tags.length);
    console.log(userInfo);

    // 因为 2s 后数据会变,我们 3s 后再看下,能够看到已经是更新后的数据了
    setTimeout(() => {
      console.log(tags);
      console.log(tags.length);
      console.log(userInfo);
    }, 3000);
  }
})

export default defineComponent({
  setup () {
    // 获取数据
    const tags: string[] = inject('tags') || [];
    const userInfo: Member = inject('userInfo') || {
      id: 0,
      name: ''
    };

    // 打印刚刚拿到的数据
    console.log(tags);
    console.log(tags.length);
    console.log(userInfo);

    // 因为 2s 后数据会变,我们 3s 后再看下,能够看到已经是更新后的数据了
    setTimeout(() => {
      console.log(tags);
      console.log(tags.length);
      console.log(userInfo);
    }, 3000);
  }
})

引用类型的数据,拿到后可以直接用,属性的值更新后,子孙组件也会被更新。
但是!!!由于不具备真正的响应性,return 给模板使用依然不会更新视图,如果涉及到视图的数据,请依然使用 响应式 API 。

7、基本类型的传递与接收 (针对非响应性数据的处理)

基本数据类型被直接 provide 出去后,再怎么修改,都无法更新下去,子孙组件拿到的永远是第一次的那个值。

先在 Grandfather.vue 里 provide 数据:

export default defineComponent({
  // ...
  setup () {
    // provide 一个数组的长度
    const tags: string[] = [ '中餐', '粤菜', '烧腊' ];
    provide('tagsCount', tags.length);

    // provide 一个字符串
    let name: string = 'Petter';
    provide('name', name);

    // 2s 后更新数据
    setTimeout(() => {
      // tagsCount 在 Grandson 那边依然是 3
      tags.push('叉烧');

      // name 在 Grandson 那边依然是 Petter
      name = 'Tom';
    }, 2000);
  }
})

在 Grandsun.vue 里 inject 拿到数据:

export default defineComponent({
  setup () {
    // 获取数据
    const name: string = inject('name') || '';
    const tagsCount: number = inject('tagsCount') || 0;

    // 打印刚刚拿到的数据
    console.log(name);
    console.log(tagsCount);

    // 因为 2s 后数据会变,我们 3s 后再看下
    setTimeout(() => {
      // 依然是 Petter
      console.log(name);

      // 依然是 3
      console.log(tagsCount);
    }, 3000);
  }
})

很失望,并没有变化。

那么是否一定要定义成响应式数据或者引用类型数据呢?

当然不是,我们在 provide 的时候,也可以稍作修改,让它能够同步更新下去。

先在 Grandfather.vue 里 provide 数据:

export default defineComponent({
  // ...
  setup () {
    // provide 一个数组的长度
    const tags: string[] = [ '中餐', '粤菜', '烧腊' ];
    provide('tagsCount', (): number => {
      return tags.length;
    });

    // provide 字符串
    let name: string = 'Petter';
    provide('name', (): string => {
      return name;
    });

    // 2s 后更新数据
    setTimeout(() => {
      // tagsCount 现在可以正常拿到 4 了
      tags.push('叉烧');

      // name 现在可以正常拿到 Tom 了
      name = 'Tom';
    }, 2000);
  }
})

再来 Grandsun.vue 里修改一下 inject 的方式,看看这次拿到的数据:

export default defineComponent({
  setup () {
    // 获取数据
    const tagsCount: any = inject('tagsCount');
    const name: any = inject('name');

    // 打印刚刚拿到的数据
    console.log(tagsCount());
    console.log(name());

    // 因为 2s 后数据会变,我们 3s 后再看下
    setTimeout(() => {
      // 现在可以正确得到 4
      console.log(tagsCount());

      // 现在可以正确得到 Tom
      console.log(name());
    }, 3000);
  }
})

这次可以正确拿到数据了,看出这2次的写法有什么区别了吗?

基本数据类型,需要 provide 一个函数,将其 return 出去给子孙组件用,这样子孙组件每次拿到的数据才会是新的。
但由于不具备响应性,所以子孙组件每次都需要重新通过执行 inject 得到的函数才能拿到最新的数据。

按我个人习惯来说,使用起来挺别扭的,能不用就不用……

由于不具备真正的响应性,return 给模板使用依然不会更新视图,如果涉及到视图的数据,请依然使用 响应式 API 。

到此这篇关于Vue3全局组件通信之provide / inject的文章就介绍到这了,更多相关Vue3全局组件通信内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Vue3.x使用mitt.js进行组件通信

    目录 快速开始 使用方式 核心原理 Vue2.x 使用 EventBus 进行组件通信,而 Vue3.x 推荐使用 mitt.js. 比起 Vue 实例上的 EventBus,mitt.js 好在哪里呢?首先它足够小,仅有200bytes,其次支持全部事件的监听和批量移除,它还不依赖 Vue 实例,所以可以跨框架使用,React 或者 Vue,甚至 jQuery 项目都能使用同一套库. 快速开始 npm install --save mitt 方式1,全局总线,vue 入口文件 main.js

  • Vue3的7种种组件通信详情

    目录 1.Vue3 组件通信方式 2.Vue3 通信使用写法 2.1 props 2.2 $emit 2.3 expose / ref 2.4 attrs 2.5 v-model 2.6 provide / inject 2.7 Vuex 1.Vue3 组件通信方式 props $emit expose / ref $attrs v-model provide / inject Vuex 2.Vue3 通信使用写法 2.1 props 用 props 传数据给子组件有两种方法,如下 方法一:混合

  • Vue3全局组件通信之provide / inject详解

    目录 1.前言 2.provide / inject 3.发起 provide 4.接收 inject 5.响应性数据的传递与接收 6.引用类型的传递与接收 (针对非响应性数据的处理) 7.基本类型的传递与接收 (针对非响应性数据的处理) 1.前言 顾名思义,爷孙组件是比 父子组件通信 要更深层次的引用关系(也有称之为 “隔代组件”): C组件引入到B组件里,B组件引入到A组件里渲染,此时A是C的爷爷级别(可能还有更多层级关系),如果你用 props ,只能一级一级传递下去,那就太繁琐了,因此我

  • vue3中的elementPlus全局组件中文转换方式

    目录 elementPlus 全局组件中文转换 elementPlus 设置默认语言为中文 elementPlus 全局组件中文转换 在项目中使用日期下拉框发现是英文的,需要全局改成中文样式. 代码实现 在 App.vue 的文件中修改即可. 代码如下 <template>     <el-config-provider :locale="locale">       <router-view></router-view>     <

  • vue3配置全局参数(挂载全局方法)以及组件的使用

    目录 vue2的方式 1. 全局挂载 2. 组件使用 vue3的方式 1. 全局挂载 2. 组件使用 vue2的方式 1. 全局挂载 Vue.property.xxx import Vue from "vue"; import axios from "axios"; Vue.prototype.$http= axios; new Vue({   router,   store,   render: (h) => h(App), }).$mount("

  • Vue3使用mitt进行组件通信的步骤

    Vue2.x使用EventBus进行组件通信,而Vue3.x推荐使用mitt.js. 比起Vue实例上的EventBus,mitt.js好在哪里呢?首先它足够小,仅有200bytes,其次支持全部事件的监听和批量移除,它还不依赖Vue实例,所以可以跨框架使用,React或者Vue,甚至jQuery项目都能使用同一套库. 1. 安装 推荐使用yarn安装(用过都知道有多丝滑) yarn add mitt 或者通过npm安装 npm install --save mitt 2. 引入到项目并挂载 可

  • VUE3学习教程之全局组件和局部组件

    目录 1. 概述 2. 全局组件 2.1 不使用组件的写法 2.2 使用组件 2.3 组件中包含变量 2.4 组件的复用 2.5 组件中使用组件 2.6 总结 3. 局部组件 3.1 局部组件的使用 3.2 总结 附:vue 3 组件的分类 全局组件与局部组件 总结 1. 概述 老话说的好:忍耐是一种策略,同时也是一种性格磨炼. 言归正传,今天我们来聊聊 VUE 的全局组件与局部组件. 2. 全局组件 2.1 不使用组件的写法 <body> <div id="myDiv&quo

  • Vue3中注册全局的组件,并在TS中添加全局组件提示方式

    目录 Vue3中注册全局的组件 Vue3踩坑--全局注册组件 我的框架:vue3+vite+ts+naiveUI Vue3中注册全局的组件 1. 在src/components中新建index.ts用来将所有需要全局注册的组件导入 : 如果使用的是 JS 可以删除类型校验 import type { Component } from 'vue' import SvgIcon from './SvgIcon/index.vue' // 如果使用的是 JS 可以删除类型校验 const compon

  • 在Vue2中注册全局组件的两种方法详解

    第一种:在main.js中直接注册 //引入 import FixedTop from '@/components/FixedTop //注册为全局组件 Vue.componet('FixedTop',FixedTop) //页面直接使用 <FixedTop /> 缺点:如果我们需要注册的全局组件非常多,那么需要一个一个引入,然后分别调用Vue.componet方法,main.js文件会变得很大很臃肿,不好维护,所以当需要注册的全局组件非常多的时候可以采用插件的形式注册 第二种:使用插件的形式

  • vue组件通信的多种方法详解

    目录 1. 父传子 2. 子传父 3. 非父子组件传值 4. vuex 5. refs 6. $children 7. $parent 8. provide & inject 9. $attrs 10. $listeners 总结 说起组件通信,可能大家耳熟能详的有以下5种: 1. 父传子 父组件通过自定义属性给子组件传值,子组件用props接收 2. 子传父 父组件在子组件标签上自定义事件,子组件通过$emit来触发自定义事件,并且可以传递参数 3. 非父子组件传值 eventBus事件总线:

  • Vue.js组件通信之自定义事件详解

    组件通信 从父组件向子组件通信,通过props传递数据就可以了,但Vue组件通信的场景不止有这一种,归纳起来,组件之间的通信可以用下图来表示: 自定义事件 当子组件需要向父组件传递数据时,就要用到自定义事件.子组件用**$ emit()来触发事件**,父组件用**$ on()**来监听子组件的事件. 父组件也可以直接在子组件的自定义标签上使用v-on来监听子组件触发的事件. <!DOCTYPE html> <html lang="en"> <head>

  • Vue中provide、inject详解以及使用教程

    目录 Vue中 常见的组件通信方式可分为三类 1. provide / inject 简介 2. provide / inject 使用方法 3. 总结 总结 传送门:Vue中 子组件向父组件传值 及 .sync 修饰符 详解 传送门:Vue中 状态管理器(vuex)详解及应用场景 传送门:Vue中 $ attrs.$ listeners 详解及使用 传送门:Vue中 事件总线(eventBus)详解及使用 传送门:Vue 2.x 官方文档 provide / inject 说明 Vue中 常见

  • 为Vue3 组件标注 TS 类型实例详解

    目录 为 props 标注类型 使用 <script setup> 非 <script setup> 为 emits 标注类型 使用 <script setup> 非 <script setup> 为 ref() 标注类型 默认推导类型 通过接口指定类型 通过泛型指定类型 为 reactive() 标注类型 默认推导类型 通过接口指定类型 为 computed() 标注类型 默认推导类型 通过泛型指定类型 为事件处理函数标注类型 为 provide / in

  • vue3.2自定义弹窗组件结合函数式调用示例详解

    目录 前言 手写弹窗组件 组件调用 函数式调用 如何使用 含样式完整源码 效果图 前言 涉及的vue3知识点/API,createApp defineProps defineEmits <script setup> v-model <script setup> 就是 setup 语法糖 defineProps 和 props 用法差不多 defineEmits 声明可向其父组件触发的事件 手写弹窗组件 很简单的弹窗组件,支持设置标题 <script setup> def

  • Vue组件之间四种通信方式详解

    目录 前言 父子组件通信​ 父组件与子孙组件的通信​ 父组件获取子组件数据​ 无需考虑组件关系的通信​ 前言 vue 框架提供了前端开发组件的思想,可以通过组件来组合成一个完整的页面,都是随着组件数量原来越多,组件之间难免需要相互通信,那么如何实现组件之间的通信呢?下面介绍 vue 组件通信的几种方法 父子组件通信​ 父组件传递 props 给子组件(数据以及改变数据的方法),子组件通过 $emit 来更新父组件的状态 父组件定义,声明状态 { name: 'baidu.com' } 以及可以改

  • Vue2.x与Vue3.x中路由钩子的区别详解

    目录 vue2.x 前置概念: 路由钩子分类 路由和组件的概念(方便理解钩子函数) 全局路由钩子 路由配置守卫钩子 组件内的守卫钩子 路由钩子执行顺序 eg: 从A组件跳转到B组件顺序 如果B路有更新, 每次都会执行以下三个钩子: vue3.x 对比变化图 区别补充: vue2.x 前置概念: 路由钩子分类 一共分3类, 7个钩子 路由和组件的概念(方便理解钩子函数) 路由和组件是2个概念, 可以粗犷的认为: 路由是浏览器网址 组件是显示在网页上的不同内容 全局路由钩子 router.befor

随机推荐