前言
关于 vue 中的 provide/inject 这一主题,它可以被看作是一种高级的依赖注入机制,允许跨层级组件实现状态共享,从而提高代码的可维护性和扩展性。在这篇文章中,我将从原理到实战带大家由浅入深探究这个机制的底层原理和具体使用方法,无论你是 vue 的初学者还是经验丰富的开发者,相信本文都将对你有所帮助。
vue 的通信方式
在 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.js中的Provide/Inject特性,这是一种允许跨层级组件共享状态的高级依赖注入机制。通过实例和代码演示,阐述了如何从基本到高级使用Provide/Inject,包括字符串传递、数据属性传递以及创建响应式数据。同时,文章也指出了Provide/Inject可能带来的维护难题,并推荐在组件库开发而非常规应用中使用,以避免数据追踪困难。
1154

被折叠的 条评论
为什么被折叠?



