Vuex 源码深度解析

Vuex 思想

在解读源码之前,先来简单了解下 Vuex 的思想。

Vuex 全局维护着一个对象,使用到了单例设计模式。在这个全局对象中,所有属性都是响应式的,任意属性进行了改变,都会造成使用到该属性的组件进行更新。并且只能通过 commit 的方式改变状态,实现了单向数据流模式。

Vuex 解析

Vuex 安装

在看接下来的内容前,推荐本地 clone 一份 Vuex 源码对照着看,便于理解。

在使用 Vuex 之前,我们都需要调用 Vue.use(Vuex) 。在调用 use 的过程中,Vue 会调用到 Vuex 的 install 函数

install 函数作用很简单

  • 确保 Vuex 只安装一次

  • 混入 beforeCreate 钩子函数,可以在组件中使用 this.$store

export function install(_Vue) {
  // 确保 Vuex 只安装一次
  if (Vue && _Vue === Vue) {
    if (process.env.NODE_ENV !== 'production') {
      console.error(
        '[vuex] already installed. Vue.use(Vuex) should be called only once.'
      )
    }
    return
  }
  Vue = _Vue
  applyMixin(Vue)
}

// applyMixin
export default function(Vue) {
  // 获得 Vue 版本号
  const version = Number(Vue.version.split('.')[0])
  // Vue 2.0 以上会混入 beforeCreate 函数
  if (version >= 2) {
    Vue.mixin({ beforeCreate: vuexInit })
  } else {
    // ...
  }
  // 作用很简单,就是能让我们在组件中
  // 使用到 this.$store
  function vuexInit() {
    const options = this.$options
    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
    }
  }
}

Vuex 初始化

this._modules

本小节内容主要解析如何初始化 this._modules

接下来看 this._modules 的过程,以 以下代码为例

对于以上代码,store 可以看成 root 。在第一次执行时,会初始化一个 rootModule,然后判断 root 中是否存在 modules 属性,然后递归注册 module 。对于 child 来说,会获取到他所属的 parent, 然后在 parent 中添加 module

installModule

接下来看 installModule 的实现

resetStoreVM

接下来看 resetStoreVM 的实现,该属性实现了状态的响应式,并且将 _wrappedGetters 作为 computed 属性。

常用 API

commit 解析

如果需要改变状态的话,一般都会使用 commit 去操作,接下来让我们来看看 commit 是如何实现状态的改变的

dispatch 解析

如果需要异步改变状态,就需要通过 dispatch 的方式去实现。在 dispatch 调用的 commit 函数都是重写过的,会找到模块内的 mutation 函数。

各种语法糖

在组件中,如果想正常使用 Vuex 的功能,经常需要这样调用 this.$store.state.xxx 的方式,引来了很多的不便。为此,Vuex 引入了语法糖的功能,让我们可以通过简单的方式来实现上述的功能。以下以 mapState 为例,其他的几个 map 都是差不多的原理,就不一一解析了。