Vuex源码浅析

前言

  Vuex是专门为Vue程序开发的状态管理模块,之前只知其然不知其所以然,今天把周末读源码的心得分享下。

一、准备实例

  先准备一个例子,在main.js中进行引用。
image.png

image.png
Vuex使用实例
image.png

二、入口文件

  鉴于目前使用的是Vue2版本,关于Vue1的就先不看了。源码中的流程:Vue.use(Vuex)–>install–>applyMinxin(Vue)–>vuexInit()

image.png
  上图是applyMinxin源码,可以看出,是在Vue实例中使用了混入,并在beforeCreate中执行了vuexInit()方法。vuexInit()方法主要是在Vue实例中添加变量:$store。逻辑就是:自己节点上(主要是根节点)有store属性,则将其赋值给this.$store,否则从父级中获取$store,这样可以保证每个vue实例中的$store都是一样的。本地复写了一个简化版。

image.png

三、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的实现:

image.png
  可以看出,dispatch只是将mutations封装了一层,在生产版上,通过actions也可以直接修改state。不建议这样做的原因,一方面是出于Promise.all()的考虑,一方面是为了方便devtools追踪变化。

参考资料

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享