👋欢迎评论,点赞,收藏哦~
😗要是有不对的地方,欢迎指正哦~
provide/inject是什么呢
在进入正文前,先讲一个真实的故事,是我N年前读到的,虽然故事的名字,主人翁和背景我都忘记了,但是一点不影响我在这陈述它😜😜😜
故事是这样的,Z家族在英国的地位非常的显赫,家族中也是人才辈出,家族里有一条规定,那就是每个人都要为后代留下一句话,话不论长短,不论对错,只要是真心的感悟,今生的沉淀都可以。在他们家族里,有一条长长的走廊,挂着祖祖辈辈留下来的人生格言,这些话都是宝贵的精神财富,就这样一代一代的传下去,影响着一批批的后人。
说到这,可以把Z家族类比成Vue组件树,每个人留下来的话,类比成provide,其后代都可以访问到。那provide就相当于一个数据提供者,其后代都有权限访问到这些数据,后代访问时通过inject API来获取,要注意的是provide和inject必须应用在一棵树上, 也就是说,从inject的节点往上回溯,得能找到与其对应的provide才行。
相比于props传值,得一层层从父级传到子级,provide/inject的方式就方便的多,可以系统的、跨越层级的传递数据,provide来提供依赖,inject来使用依赖,组件之间的接口定义仍然是明确的,而且
- 父组件不需要知道哪些子组件使用它 provide 的 property
- 子组件不需要知道 inject 的 property 来自哪里
provide/inject可以处理响应性的数据啦~
这相比Vue2中的provde/inject强大许多,因为在Vue2中它们是不拥有响应性的,
// App.vue
export default {
provide() {
return {
checkout_info: this.checkout_info,
updateCheckoutInfo: this.updateCheckoutInfo
}
},
data() {
return {
checkout_info: {},
}
},
methods: {
updateCheckoutInfo(key, value) {
this.checkout_info[key] = value
}
}
}
// SomeComponent.vue
export default {
inject: ['checkout_info', 'updateCheckoutInfo']
computed: {
deliveryAddress: {
get() { return this.checkout_info.delivery_address }, // <---- Not reactive
set(value) { return this.updateCheckoutInfo('delivery_address', value) }
}
}
}
要想变成响应性,手动使用了Object.defineProperty,当触发getter时,获取checkout_info当前最新的值,如下,
export default {
data() {
return {
checkout_info: {},
}
},
provide() {
const appData = {}
Object.defineProperty(appData, "checkout_info", {
enumerable: true,
get: () => this.checkout_info,
})
return {
updateCheckoutInfo: this.updateCheckoutInfo,
appData,
}
}
}
Reference:stackoverflow.com/questions/6…
Vue3中借助reactivity API,可以传递和操作响应性数据,举个例子👀:
当provide提供响应性数据,inject去改变这个响应性数据时,就打破了Vue中单项数据流的限制,会造成数据流向不清晰,debug困难,想一想,哪一个inject都能直接改动provide的数据,追踪变化就变得十分困难。
所以,官方就建议,对响应式 property 的所有修改限制在定义 provide 的组件内部,意思就是provide不仅提供响应性数据,还要提供方法来负责改变响应式property,inject中不直接变动数据。
举个例子👀:
Reference: v3.vuejs.org/guide/compo…
除此之外,利用readonly
包裹响应性数据也很重要哦,这样对应响应式对象就只读了,而且还是深度的作用在每个property上。
与composition API结合
我们也可以在组合式 API 中使用 provide/inject。两者都只能在当前活动实例的 setup()
期间调用。
先看一个例子,这里定义了一个composable方法,里面包含了一些api请求,这些请求以provide的形式向其所在的组件树提供接口,组件树中的组件以inject的方式使用。
👉举这个例子,想说的是,借助composition API可以把业务逻辑抽离到单独的js文件中维护,借助provide/inject API可以实现状态共享和依赖注入的功能。
与vuex对比
写到这,发现provide/inject结合composition可以做好多事,比如依赖注入,状态共享,处理响应性,逻辑复用,那这么看vuex能做的,provide/inject也能做。
举个简单的例子,看看provide/inject如何替代vuex的👀:
Reference:vuejsdevelopers.com/2020/10/05/…
我觉得最重要的一点是,vuex是全局单例的模式,意味着所有的数据是全局共享的,举个例子,a1组件实例和a2组件实例都是由A组件的构造函数生成的,那这俩实例共享一个vuex的state,也就是说,这个state状态不会以组件的维度去保存数据。要是非要一个组件实例维护一个state状态,你只能从数据上做文章,如下所示:
但是provide/inject可以做到局部多例,可以向一棵组件树提供不同的provide/inject数据 ( 以provide第一个参数name为区分), 也可以向不同的组件树提供数据。
如图所示,在同一个Vue根实例里,provide/inject可以以局部多列的模式存在(一类颜色代表一种依赖注入)
小结👉: