前言
Vuex作为VUE生态全家桶的一员,相信大部分使用VUE的前端开发工程师都会用过Vuex来做状态管理。可是大家有没有了解过Vuex的实现原来是怎样的呢?
Vuex是做什么的?
用官方的话来说,Vuex是一个“状态管理”。其实所谓的状态管理就是一个全局的变量管理实例。只是状态管理会给这些数据的修改做一定的规范。
Vuex的设计是借鉴了Redux和Flux的,因此我们可以看到如果想要在Vuex中修改的数据的时候,我们需要通过mutation来操作。通过一系列的规范,可以使得状态(数据)的修改变得更容易追踪。
Vuex的优势是什么?
其实这个问题尤大本人是回应的。
可以看到,VUEX其实与如Reduex等状态管理库相似,最大的区别是它集成了Vue的双向绑定,以及在vue组件中的使用会更加便利。
Vuex的实现原理
介绍完Vuex的本质之后,接下来我们来深入一下Vuex的原理。首先我们来回顾一下,Vuex的使用方法:
import Vue from 'vue';
import App from './App';
import router from './router';
import store from './store'; // 引入我们前面导出的store对象
Vue.use(Vuex);
const store = new Vuex.Store({
state: {
// ....管理的数据
},
});
new Vue({
el: '#app',
router,
store, // 把store对象添加到vue实例上
components: { App },
template: '<App/>'
});
可以理解成,使用VUEX的步骤分为2步
// 声明式调用Vuex
Vue.use(Vuex);
// 声明要管理的数据
new Vue({
store:new Vuex.Store({
// ...
})
})
Vue.use(Vuex)
先来看use的这部份,我们都知道Vue.use会调用入参对象的install方法。这里Vuex的install方法源码如下:
// src/store.js
export function install (_Vue) {
if (Vue && _Vue === Vue) {
return
}
Vue = _Vue
applyMixin(Vue)
}
接下来看一下这个applyMixin方法的源码
export default function (Vue) {
const version = Number(Vue.version.split('.')[0])
if (version >= 2) {
// 这里做了一个全局的mixin
Vue.mixin({ beforeCreate: vuexInit })
} else {
const _init = Vue.prototype._init
Vue.prototype._init = function (options = {}) {
options.init = options.init
? [vuexInit].concat(options.init)
: vuexInit
_init.call(this, options)
}
}
可以看到install里做的是一个全局的mixin,。目标是beforeCreate生命周期,接下来再看看vuexInit函数的内容。
function vuexInit () {
const options = this.$options
// 往当前实例挂入store
if (options.store) {
this.$store = typeof options.store === 'function'
? options.store()
: options.store
} else if (options.parent && options.parent.$store) {
this.$store = options.parent.$store
}
}
}
可以看到通过上方一系列的通知之后,在每个vue组件实例实例化之后,在beforeCreate生命周期都会往当前实例挂入store。这个store是来自于$optiopn。也就是我们在VUE渲染入口时,定义的store实例对象。
这也解释了为什么我们可以在vue组件中通this.$store拿到store数据。
Vuex的双向绑定
当我们在组件中用到store的数据时,在数据修改后相关的逻辑(视图)会自动更新。这其实是因为Vuex在store实例中,帮我们实现了双休绑定。核心思路跟Vue中MVVM实现的套路是一致的,底层用的还是Object.defineProperty。更多相关的内容可以看我之前关于MVVM的那篇文章) —— [VUE] 模仿vue实现一个mvvm框架。
在Vuex中这部分的源码如下:
// src/core/observer/index.js
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter () {
const value = getter ? getter.call(obj) : val
// Dep.target是一个是否进行加入订阅的标识,同时本身也是一个watcher实例。
if (Dep.target) {
dep.depend()
if (childOb) {
childOb.dep.depend()
}
if (Array.isArray(value)) {
dependArray(value)
}
}
return value
},
set: function reactiveSetter (newVal) {
const value = getter ? getter.call(obj) : val
if (newVal === value || (newVal !== newVal && value !== value)) {
return
}
if (setter) {
setter.call(obj, newVal)
} else {
val = newVal
}
childOb = !shallow && observe(newVal)
// 每次当前属性被修改后,会调用dep.notify()。从而触发订阅更新
dep.notify()
}
})
总结
Vuex是Vue生态中的重要一员,今天我们深入了解了一下Vuex的实现原理。相信大家在了解过后都会发现,其实Vuex的实现并不复杂(当然具体的开发下肯定还会有很多的细节)。了解Vuex的原理,可以让我们在开发使用时,对Vuex会一个更加清晰的认识,在出现bug时可以有更多的处理思路。