何为 provide/inject

provide/inject 是 vue 在 2.2.0 版本新增的 API,官网介绍如下:

这对选项需要一起使用,以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在起上下游关系成立的时间里始终生效。如果你熟悉 react,这与 react 的上下文特性很相似。

 

使用 provide/inject 做全局状态管理

在官网文档中关于 provide/inject 有这么一个提示:

提示:provide 和 inject 绑定并不是可响应的。这是刻意为之的。然而,如果你传入了一个可监听的对象,那么其对象的属性还是可响应的。

也就是说,vue 不会对 provide 中的变量进行响应式处理。所以,要想 inject 接受的变量是响应式的,provide 提供的变量本身就需要是响应式的。

由于组件内部的各种状态就是可响应的,所以我们直接在根组件中将组件本身注入 provide,此时,我们可以在后代组件中任意访问根组件中的所有状态,根组件就成为了全局状态的容器,仔细想想,是不是很像 React 中的 context 呢?

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>provide/inject实现状态管理</title>
<script src="https://cdn.bootcss.com/vue/2.6.11/vue.min.js"></script>
</head>
<body>
<div id="app"></div>
</body>
</html>

<script>
Vue.component('A', {
template: `
<div>
<p><label>name: </label><span>{{ data.name }}</span></p>
<p><label>age: </label><span>{{ data.age }}</span></p>
<B></B>
<C></C>
</div>
`,
data() {
return {
data: {
name: '',
age: ''
}
}
},
provide() {
return {
// 因为data属性是响应式的(vue2使用Object.defineProperty/vue3使用proxy)
data: this.data
}
}
})
Vue.component('B', {
template: `
<div>
<button @click="changeName">changeName</button>
</div>
`,
inject: ['data'],
methods: {
changeName() {
this.data.name = 'tom'
}
}
})
Vue.component('C', {
template: `
<div>
<button @click="changeAge">changeAge</button>
</div>
`,
inject: ['data'],
methods: {
changeAge() {
this.data.age = 20
}
}
})
var app=new Vue({
el: '#app',
template: `
<div>
<A />
</div>
`
});
</script>


vuex状态可以分模块管理,provide/inject也可以实现,使用根组件包裹每一个模块,每个模块的根组件管理该模块的状态。

provide/inject实现状态管理与vuex区别

既然 provide/inject 如此好用,那么,为什么 Vue 官方还要推荐我们使用 Vuex,而不是用原生的 API 呢?

Vuex 和 provide/inject 最大的区别在于,Vuex 中的全局状态的每次修改是可以追踪回溯的,而 provide/inject 中变量的修改是无法控制的,换句话说,你不知道是哪个组件修改了这个全局状态。