平常我们的全局状态管理通通都交给
Vuex来,Vuex也的确是非常好的工具,并被官方作为指定的状态管理器。今天给大家介绍有个新的思路进行状态管理。可能大家对provide / inject这对api有点陌生,的确也是,平常开发中我们基本也不会用到。

看图片可以知道,provide期望传入一个Object,而inject可以接受的值包括(1)一个字符串数组
(2)一个对象。代码如下:(最简单的写法)
// 父组件 App.vue
export default {
name: 'App',
provide: {
parentKey: 'aaa'
}
}
// 子组件 chidlren.vue
export default {
inject: ['parentKey'],
created () {
console.log(this.parentKey)
}
}
在打印台可以看到aaa被输出了出来,既然inject期望值是个数组,那应该也可以这样
// 父组件 App.vue
export default {
name: 'App',
provide: {
parentKey: { a: 'haha' },
parentKey2: [1, 2]
}
}
// 子组件 chidlren.vue
export default {
inject: ['parentKey', 'parentKey2'],
created () {
console.log(this.parentKey, this.parentKey2)
}
}
之前我们传递是个字符串,发现其实传递什么都可以,对应的对象和数组都打印出来了。 如果仅仅只是这样,好像也没什么。看到api有这样一句话:
提示:
provide和inject绑定并不是可响应的。这是刻意为之的。然而,如果你传入了一个可监听的对象,那么其对象的属性还是可响应的。
我们可以试下:
// 父组件 App.vue
<template>
<div class="app">
<router-view></router-view>
<button @click="changeKey"> 按钮</button>
</div>
</template>
export default {
provide () {
return {
parentKey: this.key
}
},
methods: {
changeKey () {
this.key.push(2)
}
},
data () {
return {
key: [1]
}
}
}
// 子组件 chidlren.vue
export default {
inject: ['parentKey'],
created () {
let afterKey = JSON.stringify(this.parentKey)
let timer = setInterval(() => {
console.log(this.parentKey)
if (afterKey !== JSON.stringify(this.parentKey)) clearInterval(timer)
}, 1000)
}
}
最后可以看见当点击按钮之后,在子组件chidlren.vue中输出的值变化了,定时器也停止了。
这里面有几个要注意的地方
(1). 这时候provide必须为一个工厂函数,如果你是这样写的provide: {parentKey: this.key}也就是跟之前这种写法,控制台会报未定义key的错误
(2)不要传字符串之类的值过去,比如:parentKey: this.key 而你在data中是这样定义key的key: 'aaa,提示下,他并不是一个可响应的数据,你可能会说,那不对啊,我在当前组件改变这个值,模板里输出的也会变啊。这时候其实可响应的值是this当前组件
(3)大家不要试图替换数组来响应(为什么说这条,因为不注意我也犯了这个错误 :)
(4)一旦注入了某个数据,比如上面示例中的 parentKey,那这个组件中就不能再声明 parentKey 这个数据了,因为它已经被父级占有)
注意:在变异 (不是替换) 对象或数组时,旧值将与新值相同,因为它们的引用指向同一个对象/数组。Vue 不会保留变异之前值的副本。(来源Vue官网)
好像说了这么久,没说到与标题相关的。:)-- 接下来久介绍下构建全局状态管理的思路
// 父组件(也可以说是根组件) App.vue
export default {
name: 'App',
provide () {
return {
app: this
}
},
methods: {
changeKey () {
this.key.push(2)
}
},
data () {
return {
key: [1],
transitionName: 'slide-right'
}
}
}
// 子组件 chidlren.vue
export default {
inject: ['app'],
created () {
console.log(this.app)
console.log(this.app.key)
this.app.changeKey()
}
}
这样你就可以拿到根节点app所有的东西了,还可以访问他的方法。以此将根节点作为状态管理器。
如果你觉得这样状态多起来,十分难管理,你可以使用混合mixins,将不同的逻辑分开到不同的 js 文件里。当然这种事是仁者见仁智者见智,你可以使用this.$root访问根节点,还可以挂载在Vue.prototype的原型上。哈哈,这些方法是不是感觉有点邪门歪道的感觉。所以官网有这么一句话:
provide 和 inject 主要为高阶插件/组件库提供用例。并不推荐直接用于应用程序代码中。
给大家介绍下这个网站dev.iviewui.com,这个网站就是使用此方法做出来的,这样减少了Vuex的依赖使用vue本身作为状态管理器,当然,如果你的网站很大,还是建议使用Vuex。
这对api还有很多东西可以挖掘的,比如使用ES2015的Symbol作为唯一值,inject中可以使用default作为默认值等等。用法还是同学们去看看官网。这次久到此为止,清明放假了,还是休息休息。:)
如有错误,欢迎指出。