详解vue中的provide/inject

Posted 水星记_

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了详解vue中的provide/inject相关的知识,希望对你有一定的参考价值。

前言

vue 中组件之间交互通信有很多种方式,props传值、emit传值、bus传值等等,相信大多数同学对此都不陌生,不太清楚的同学也可以 『点此』 查看博主往期的文章,其中有详细的讲解。话说回来,当组件的层次结构比较深时,propsemit 就没什么作用了。这个时候,vue 提出了 Provide / Inject


1、Provide / Inject是什么?

文章开头这张图,很好地诠释了 provide/inject 的核心,但是可能很多同学并没有看出其中端倪,别急,文章最后,还是这张图,你一定会恍然大悟。

provide/inject 这对选项需要一起使用,以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在其上下游关系成立的时间里始终生效。通俗说就是可以用父组件给祖孙组件进行传值,也就是可以隔代传值,不论子组件有多深,只要调用了 inject 那么就可以注入 provider 中的数据,而不是局限于只能从当前父组件的 props 属性来获取数据,这也是 provide/inject 最大的特性。

provide 提供变量

provide 是一个对象,或者是一个返回对象的函数。里面呢就包含要给子孙后代的东西,也就是属性和属性值。

inject 注入变量

inject 是一个字符串数组,或者是一个对象。属性值可以是一个对象,包含 formdefault 默认值。


类型

provide

Object | () => Object

inject

Array<string> |  [key: string]: string | Symbol | Object 

代码执行顺序

data->provide->created(在这个阶段$el还未生成,在这先处理privide的逻辑,子孙组件才可以取到inject的值)->mounted

注意:

provideinject 绑定并不是可响应的。这是刻意为之的。然而,如果你传入了一个可监听的对象,那么其对象的 property 还是可响应的。根据官方要求,我们只需要传入对象过去,并且对象是响应式的,定义在 data 或者计算属性里都可以。


2、实例

相信看完上面的内容,你已经对 provideinject 有了初步的认识和了解,但是说了这么多,终究都是理论,秉持着实践出真知的原则,下面我们进行代码实践。


文件结构


2.1 初阶用法 ---- 字符串传递

在父组件中 provide 提供变量

文件目录:src/views/monitors/index.vue

<template>
  <!-- 父组件 -->
  <div>
    <Child></Child>
  </div>
</template>

<script>
import Child from "./seed/index";
export default 
  components: 
    Child,
  ,
  provide: 
    message: "传给孙组件的值",
  ,
  data() 
    return ;
  ,
;
</script>

在子组件中,我们不使用任何父组件的信息

文件目录:src/views/monitors/seed/index.vue

<template>
  <!-- 子组件 -->
  <div>
    <el-dialog :visible.sync="centerDialogVisible" width="30%" center>
      <grandson />
    </el-dialog>
  </div>
</template>

<script>
import grandson from "./grandson/index";
export default 
  components: 
    grandson,
  ,
  data() 
    return 
      centerDialogVisible: true,
    ;
  ,
;
</script>

在孙组件中,使用 inject 来注入

文件目录:src/views/monitors/seed/grandson/index.vue

<template>
  <!-- 孙组件 -->
  <div>
    message 
  </div>
</template>

<script>
export default 
  inject: ["message"],
  data() 
    return ;
  ,
;
</script>

页面效果


2.2 中阶用法 ---- 传递data中属性

上面的例子我们是在 provide 中定义的一个字符串,那如果要传 data 里的一个属性呢?这个时候就需要稍微修改一下组件,provide 需要用 returen 的写法。

需要在父组件中修改

文件目录:src/views/monitors/index.vue

<template>
  <!-- 父组件 -->
  <div>
    <Child></Child>
  </div>
</template>

<script>
import Child from "./seed/index";
export default 
  components: 
    Child,
  ,
  provide() 
    return 
      message: this.message,
    ;
  ,
  data() 
    return 
      message: "传给孙组件的值",
    ;
  ,
;
</script>

子组件和孙组件依旧保持不变。

页面效果


2.3 高阶用法 ---- 响应式数据

文章开头我们提到了 provideinject 绑定并不是可响应的。 那如何变成响应式的呢,再简单改一下。

还是在父组件中修改

文件目录:src/views/monitors/index.vue

<template>
  <!-- 父组件 -->
  <div>
    <Child></Child>
  </div>
</template>
  
<script>
import Child from "./seed/index";
export default 
  components: 
    Child,
  ,
  provide() 
    return 
      message: this.obj,
    ;
  ,
  data() 
    return 
      obj: 
        num: 0,
      ,
    ;
  ,
  created() 
    setInterval(() => 
      this.obj.num++;
    , 1000);
  ,
;
</script>

子组件依旧不变

孙组件中稍加修改

文件目录:src/views/monitors/seed/grandson/index.vue

<template>
  <!-- 孙组件 -->
  <div>
    message.num
  </div>
</template>

<script>
export default 
  inject: ["message"],
  data() 
    return ;
  ,
;
</script>

页面效果

注意:

传过去的必须是可监听的对象,其他类型都不行。


2.3.1 传递 this

上面的操作,我们只是取了 data 中的一个对象,当然,你可以直接传一个 this 过去,这样孙组件就会获得爷爷组件的实例对象,且这种方式也是响应式的。

仍然是在父组件中修改

文件目录:src/views/monitors/index.vue

<template>
  <!-- 父组件 -->
  <div>
    <Child></Child>
  </div>
</template>

<script>
import Child from "./seed/index";
export default 
  components: 
    Child,
  ,
  provide() 
    return 
      message: this,
    ;
  ,
  data() 
    return 
      obj: 
        num: 0,
      ,
    ;
  ,
  created() 
    setInterval(() => 
      this.obj.num++;
    , 1000);
  ,
;
</script>

子组件依旧不变

孙组件中再次稍加修改

文件目录:src/views/monitors/seed/grandson/index.vue

<template>
  <!-- 孙组件 -->
  <div>
    message.obj.num
  </div>
</template>

<script>
export default 
  inject: ["message"],
  data() 
    return ;
  ,
;
</script>

页面效果


小结


通过一系列的理论+实践,现在我们回过头再来看文章开头的这张图是不是就很清晰了,但是可能有人问了,难道 provide/inject 就没有什么缺点吗?咱们接着往下看。


3、provide/inject 的缺点

大家都知道,在项目中通常追求有清晰的数据流向和合理的组件层级关系,以便于调试和维护,然而 provideinject 支持任意层级都能访问的特性,导致数据追踪比较困难,你压根不知道是哪一个层级声明了 provide,或者不知道哪一个层级或若干个层级使用了 inject,后期容易造成比较大的维护成本。因此,provideinject 在常规应用下并不建议使用,vue 更建议使用 vuex 解决。但是在做组件库开发时,不对 vuex 进行依赖,且不知道用户使用环境的情况下可以很好的使用 provideinject。官方也着重说明 provideinject 主要为高阶插件/组件库提供用例,并不推荐直接用于应用程序代码中。

以上是关于详解vue中的provide/inject的主要内容,如果未能解决你的问题,请参考以下文章

vue3如何进行数据依赖注入provide/inject

vue组件通讯之provide / inject

vue 初步了解provide/inject

Vue小技能:在 setup() 中使用provide/ inject

vue 父组件如何给子组件的组件传值 provide inject

vue的Provide/inject 和 React 的 Context