前言
Vuex是专门为Vue程序开发的状态管理模块,之前只知其然不知其所以然,今天把周末读源码的心得分享下。
一、准备实例
先准备一个例子,在main.js中进行引用。
Vuex使用实例
二、入口文件
鉴于目前使用的是Vue2版本,关于Vue1的就先不看了。源码中的流程:Vue.use(Vuex)–>install–>applyMinxin(Vue)–>vuexInit()
上图是applyMinxin源码,可以看出,是在Vue实例中使用了混入,并在beforeCreate中执行了vuexInit()方法。vuexInit()方法主要是在Vue实例中添加变量:$store。逻辑就是:自己节点上(主要是根节点)有store属性,则将其赋值给this.$store,否则从父级中获取$store,这样可以保证每个vue实例中的$store都是一样的。本地复写了一个简化版。
三、state
分析state的特性,剔除源码中处理module的部分,核心代码如下:
class Store {
constructor(options){
// state
this._vm=new Vue({
data() {
return {
state:options.state
}
}
})
}
// 私有变量
get state(){
return this._vm.state
}
}
复制代码
构造函数中,创建了一个Vue实例,并将options中的state绑定在data中,并在类中创建了state属性,返回Vue实例中的data。
四、getters
getters就像Vue中的过滤器,将state中的值过滤下并返回。getters也是Store对象中的属性,所以,可以将options中的getters进行复制,并将其返回值进行返回。这里利用了Object.defineProperty方法。复写如下:
// getters
let getters=options.getters||{}
this.getters={}
Object.keys(getters).forEach(key=>{
Object.defineProperty(this.getters,key,{
get:()=>{
return getters[key](this.state)
}
})
})
复制代码
这里特意用箭头函数,是为了this的指向。
五、mutations
mutations中存储的是同步更改state的方法。复写如下:
// mutations
let mutations=options.mutations||{}
this.mutations={}
Object.keys(mutations).forEach(key=>{
this.mutations[key]=(params)=>{
mutations[key](this.state,params)
}
})
复制代码
上述代码,this.mutations利用了函数柯里化,把state当做固定参数传进去
。
commit(type,params){
this.mutations[type](params)
}
复制代码
commit执行就很简单了。
六、actions
actions代码结构整体和mutations差不多,但多了异步封装。
function registerAction (store, type, handler, local) {
const entry = store._actions[type] || (store._actions[type] = [])
entry.push(function wrappedActionHandler (payload) {
let res = handler.call(store, {
dispatch: local.dispatch,
commit: local.commit,
getters: local.getters,
state: local.state,
rootGetters: store.getters,
rootState: store.state
}, payload)
if (!isPromise(res)) {
res = Promise.resolve(res)
}
if (store._devtoolHook) {
return res.catch(err => {
store._devtoolHook.emit('vuex:error', err)
throw err
})
} else {
return res
}
})
}
复制代码
如上是注册action的代码,返回的res一定是个Promise对象,为什么要这样设计?主要是为了能够一个 action 执行完能够使用 then 继续处理下一个异步操作。来看dispatch的实现:
可以看出,dispatch只是将mutations封装了一层,在生产版上,通过actions也可以直接修改state。不建议这样做的原因,一方面是出于Promise.all()的考虑,一方面是为了方便devtools追踪变化。