详解vue中的provide/inject
Posted 水星记_
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了详解vue中的provide/inject相关的知识,希望对你有一定的参考价值。
前言
在
vue
中组件之间交互通信有很多种方式,props
传值、emit
传值、bus
传值等等,相信大多数同学对此都不陌生,不太清楚的同学也可以 『点此』 查看博主往期的文章,其中有详细的讲解。话说回来,当组件的层次结构比较深时,props
和emit
就没什么作用了。这个时候,vue
提出了Provide / Inject
。
1、Provide / Inject是什么?
文章开头这张图,很好地诠释了 provide/inject
的核心,但是可能很多同学并没有看出其中端倪,别急,文章最后,还是这张图,你一定会恍然大悟。
provide/inject
这对选项需要一起使用,以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在其上下游关系成立的时间里始终生效。通俗说就是可以用父组件给祖孙组件进行传值,也就是可以隔代传值,不论子组件有多深,只要调用了 inject
那么就可以注入 provider
中的数据,而不是局限于只能从当前父组件的 props
属性来获取数据,这也是 provide/inject
最大的特性。
provide 提供变量
provide
是一个对象,或者是一个返回对象的函数。里面呢就包含要给子孙后代的东西,也就是属性和属性值。
inject 注入变量
inject
是一个字符串数组,或者是一个对象。属性值可以是一个对象,包含 form
和 default
默认值。
类型
provide
Object | () => Object
inject
Array<string> | [key: string]: string | Symbol | Object
代码执行顺序
data->provide->created(在这个阶段$el还未生成,在这先处理privide的逻辑,子孙组件才可以取到inject的值)->mounted
注意:
provide
和 inject
绑定并不是可响应的。这是刻意为之的。然而,如果你传入了一个可监听的对象,那么其对象的 property
还是可响应的。根据官方要求,我们只需要传入对象过去,并且对象是响应式的,定义在 data
或者计算属性里都可以。
2、实例
相信看完上面的内容,你已经对 provide
和 inject
有了初步的认识和了解,但是说了这么多,终究都是理论,秉持着实践出真知的原则,下面我们进行代码实践。
文件结构
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 高阶用法 ---- 响应式数据
文章开头我们提到了 provide
和 inject
绑定并不是可响应的。 那如何变成响应式的呢,再简单改一下。
还是在父组件中修改
文件目录: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 的缺点
大家都知道,在项目中通常追求有清晰的数据流向和合理的组件层级关系,以便于调试和维护,然而 provide
和 inject
支持任意层级都能访问的特性,导致数据追踪比较困难,你压根不知道是哪一个层级声明了 provide
,或者不知道哪一个层级或若干个层级使用了 inject
,后期容易造成比较大的维护成本。因此,provide
和 inject
在常规应用下并不建议使用,vue
更建议使用 vuex
解决。但是在做组件库开发时,不对 vuex
进行依赖,且不知道用户使用环境的情况下可以很好的使用 provide
和 inject
。官方也着重说明 provide
和 inject
主要为高阶插件/组件库提供用例,并不推荐直接用于应用程序代码中。
以上是关于详解vue中的provide/inject的主要内容,如果未能解决你的问题,请参考以下文章
Vue小技能:在 setup() 中使用provide/ inject