目录
1、前言
2、provide / inject
3、发起 provide
4、接收 inject
5、响应性数据的传递与接收
6、引用类型的传递与接收 (针对非响应性数据的处理)
7、基本类型的传递与接收 (针对非响应性数据的处理)
首页 web前端 Vue.js Vue3全局组件通信之provide/inject源码分析

Vue3全局组件通信之provide/inject源码分析

May 14, 2023 pm 05:58 PM
vue3 provide inject

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 个参数:

参数类型说明
keystring数据的名称
valueany数据的值

来看一下如何创建一个 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 个参数:

参数类型说明
keystring与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>(&#39;Hello World!&#39;);
    provide(&#39;msg&#39;, msg);

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

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

      // 修改用户名
      userInfo.name = &#39;Tom&#39;;
    }, 2000);
  }
})
登录后复制

在 Grandsun.vue 里 inject 拿到数据:

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

    // 打印刚刚拿到的数据
    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[] = [ &#39;中餐&#39;, &#39;粤菜&#39;, &#39;烧腊&#39; ];
    provide(&#39;tags&#39;, tags);

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

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

      // 修改userInfo的属性值
      userInfo.name = &#39;Tom&#39;;
    }, 2000);
  }
})
登录后复制

在 Grandsun.vue 里 inject 拿到数据:

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

    // 打印刚刚拿到的数据
    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(&#39;tags&#39;) || [];
    const userInfo: Member = inject(&#39;userInfo&#39;) || {
      id: 0,
      name: &#39;&#39;
    };

    // 打印刚刚拿到的数据
    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[] = [ &#39;中餐&#39;, &#39;粤菜&#39;, &#39;烧腊&#39; ];
    provide(&#39;tagsCount&#39;, tags.length);

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

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

      // name 在 Grandson 那边依然是 Petter
      name = &#39;Tom&#39;;
    }, 2000);
  }
})
登录后复制

在 Grandsun.vue 里 inject 拿到数据:

export default defineComponent({
  setup () {
    // 获取数据
    const name: string = inject(&#39;name&#39;) || &#39;&#39;;
    const tagsCount: number = inject(&#39;tagsCount&#39;) || 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[] = [ &#39;中餐&#39;, &#39;粤菜&#39;, &#39;烧腊&#39; ];
    provide(&#39;tagsCount&#39;, (): number => {
      return tags.length;
    });

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

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

      // name 现在可以正常拿到 Tom 了
      name = &#39;Tom&#39;;
    }, 2000);
  }
})
登录后复制

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

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

    // 打印刚刚拿到的数据
    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源码分析的详细内容。更多信息请关注PHP中文网其他相关文章!

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热门文章

R.E.P.O.能量晶体解释及其做什么(黄色晶体)
3 周前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳图形设置
3 周前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您听不到任何人,如何修复音频
3 周前 By 尊渡假赌尊渡假赌尊渡假赌
WWE 2K25:如何解锁Myrise中的所有内容
4 周前 By 尊渡假赌尊渡假赌尊渡假赌

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

vue3+vite:src使用require动态导入图片报错怎么解决 vue3+vite:src使用require动态导入图片报错怎么解决 May 21, 2023 pm 03:16 PM

vue3+vite:src使用require动态导入图片报错和解决方法vue3+vite动态的导入多张图片vue3如果使用的是typescript开发,就会出现require引入图片报错,requireisnotdefined不能像使用vue2这样imgUrl:require(’…/assets/test.png’)导入,是因为typescript不支持require所以用import导入,下面介绍如何解决:使用awaitimport

vue3项目中怎么使用tinymce vue3项目中怎么使用tinymce May 19, 2023 pm 08:40 PM

tinymce是一个功能齐全的富文本编辑器插件,但在vue中引入tinymce并不像别的Vue富文本插件一样那么顺利,tinymce本身并不适配Vue,还需要引入@tinymce/tinymce-vue,并且它是国外的富文本插件,没有通过中文版本,需要在其官网下载翻译包(可能需要翻墙)。1、安装相关依赖npminstalltinymce-Snpminstall@tinymce/tinymce-vue-S2、下载中文包3.引入皮肤和汉化包在项目public文件夹下新建tinymce文件夹,将下载的

Vue3怎么解析markdown并实现代码高亮显示 Vue3怎么解析markdown并实现代码高亮显示 May 20, 2023 pm 04:16 PM

Vue实现博客前端,需要实现markdown的解析,如果有代码则需要实现代码的高亮。Vue的markdown解析库有很多,如markdown-it、vue-markdown-loader、marked、vue-markdown等。这些库都大同小异。这里选用的是marked,代码高亮的库选用的是highlight.js。具体实现步骤如下:一、安装依赖库在vue项目下打开命令窗口,并输入以下命令npminstallmarked-save//marked用于将markdown转换成htmlnpmins

Vue3如何实现刷新页面局部内容 Vue3如何实现刷新页面局部内容 May 26, 2023 pm 05:31 PM

想要实现页面的局部刷新,我们只需要实现局部组件(dom)的重新渲染。在Vue中,想要实现这一效果最简便的方式方法就是使用v-if指令。在Vue2中我们除了使用v-if指令让局部dom的重新渲染,也可以新建一个空白组件,需要刷新局部页面时跳转至这个空白组件页面,然后在空白组件内的beforeRouteEnter守卫中又跳转回原来的页面。如下图所示,如何在Vue3.X中实现点击刷新按钮实现红框范围内的dom重新加载,并展示对应的加载状态。由于Vue3.X中scriptsetup语法中组件内守卫只有o

Vue3复用组件怎么使用 Vue3复用组件怎么使用 May 20, 2023 pm 07:25 PM

前言无论是vue还是react,当遇到多处重复代码的时候,我们都会想着如何复用这些代码,而不是一个文件里充斥着一堆冗余代码。实际上,vue和react都可以通过抽组件的方式来达到复用,但如果遇到一些很小的代码片段,你又不想抽到另外一个文件的情况下,相比而言,react可以在相同文件里面声明对应的小组件,或者通过renderfunction来实现,如:constDemo:FC=({msg})=>{returndemomsgis{msg}}constApp:FC=()=>{return(

Vue3中怎么实现选取头像并裁剪 Vue3中怎么实现选取头像并裁剪 May 29, 2023 am 10:22 AM

最终效果安装VueCropper组件yarnaddvue-cropper@next上面的安装值针对Vue3的,如果时Vue2或者想使用其他的方式引用,请访问它的npm官方地址:官方教程。在组件中引用使用时也很简单,只需要引入对应的组件和它的样式文件,我这里没有在全局引用,只在我的组件文件中引入import{userInfoByRequest}from'../js/api'import{VueCropper}from'vue-cropper&

Vue3中如何使用defineCustomElement定义组件 Vue3中如何使用defineCustomElement定义组件 May 28, 2023 am 11:29 AM

使用Vue构建自定义元素WebComponents是一组web原生API的统称,允许开发者创建可复用的自定义元素(customelements)。自定义元素的主要好处是,它们可以在使用任何框架,甚至是在不使用框架的场景下使用。当你面向的最终用户可能使用了不同的前端技术栈,或是当你希望将最终的应用与它使用的组件实现细节解耦时,它们会是理想的选择。Vue和WebComponents是互补的技术,Vue为使用和创建自定义元素提供了出色的支持。你可以将自定义元素集成到现有的Vue应用中,或使用Vue来构

怎么使用vue3+ts+axios+pinia实现无感刷新 怎么使用vue3+ts+axios+pinia实现无感刷新 May 25, 2023 pm 03:37 PM

vue3+ts+axios+pinia实现无感刷新1.先在项目中下载aiXos和pinianpmipinia--savenpminstallaxios--save2.封装axios请求-----下载js-cookienpmiJS-cookie-s//引入aixosimporttype{AxiosRequestConfig,AxiosResponse}from"axios";importaxiosfrom'axios';import{ElMess

See all articles