最近在使用Vuex的时候,把Vuex和NGRX一起对比看了下,总体而言,都是对FLUX 思想的实现,不过使用起来,个人感觉vuex的使用比NGRX更舒服一些。
下面介绍一些Vuex的实现原理, 本文只是实现一个最简单的类vuex,为了有助于理解vuex的工作方式,实际vuex的实现要比这复杂的多。
先放代码,在分块讲解:
const Store = function Store (options = {}) {
const {state = {}, mutations={}, getters={},actions={}} = options
const computed = {}
const store = this
store.getters = {};
for (let [key, fn] of Object.entries(getters)) {
computed[key] = function () { return fn(store.state, store.getters); };
Object.defineProperty(store.getters, key, {
get: function () { return store._vm[key]; },
});
}
this._vm = new Vue({
data: {
$$state: state
},
computed,
})
this._mutations = mutations
this._actions = actions
}
Store.prototype.commit = function(type, payload){
if(this._mutations[type]) {
this._mutations[type](this.state, payload)
}
}
Store.prototype.dispatch = function(type){
if(this._actions[type]) {
const store = this;
this._actions[type]({commit:this.commit.bind(store)});
}
}
Object.defineProperties(Store.prototype, {
state: {
get: function(){
return this._vm._data.$$state
}
}
});
在Vue框架中,当你把数据定义在Data属性下的时候,里面的属性就可以做到响应式的监听,其实是通过的Proxy做的,也就是常说的
Object.defineProperty(obj, prop, value);
因此,在Vue中实现Vuex,我们完全可以应用这个想法,把你的state 放到Vue实例的data属性里:
this._vm = new Vue({
data: {
$$state: state
},
这里面的 state就是我们在使用Vuex中传入的state,这样在我们使用的时候,它的监听和通知就由Vue框架在实例化Vue组件的时候帮我们做好了,其实是在new Vue() 这个函数里做的(感兴趣的后面可以分开一篇来写)。
还有个问题,大家在使用的时候知道,我们是直接获得的state属性,并没有调用什么"_vm.$$state"啊什么的,这其实很简单:
Object.defineProperties(Store.prototype, {
state: {
get: function(){
return this._vm._data.$$state
}
}
});
我们只需定义下state的属性获取就可以了。
Store是一个大的状态容器,(说白了可以简化成Object),那么我们规定你只能通过mutation去更改数据,并且mutation是纯函数,不含异步操作的。
Store.prototype.commit = function(type, payload){
if(this._mutations[type]) {
this._mutations[type](this.state, payload)
}
}
dispatch 通常是用来trigger action 的,那么dispatch的实现又是什么样的呢?
Store.prototype.dispatch= function(type, payload){
if(this._actions[type]) {
this._actions[type]({this.commit.bind(this)}, payload)
}
}
大家在用getters的时候发现确实很方便,那它又是如何实现的呢?
getters其实是借用了vue的computed属性,这样它还做到缓存:
store.getters = {};
for (let [key, fn] of Object.entries(getters)) {
computed[key] = function () { return fn(store.state, store.getters); };
Object.defineProperty(store.getters, key, {
get: function () { return store._vm[key]; },
});
}
结合最开始的创建Vue组件,我们要把computed注册到vue组件的computed属性上:
this._vm = new Vue({
data: {
$$state: state
},
computed,
})
这就是 Vuex的基本实现形式,当然肯定和Vuex源码有不一样的地方,比如modules的实现,比如actions的param实际是context,等等。大家感兴趣的可以再深入去读vuex的源码。