总结与常见问题
到此我们已经将Vuex 3.0.1的源码已经全部解读完毕了。本文对整个源码进行总结,并列举一些常见问题。
# 一、总结
# 1. Vuex是一个Vue插件
Vuex 是一个状态管理库,也是一个Vue插件,它将Vue应用中的共享状态抽取出来,以集中式的方式管理。所有的Vue插件都应该是一个函数或者是一个具有install方法的对象。Vuex就是一个具有install方法的对象,当我们执行Vue.use(Vuex)时就会调用Vuex.install方法,将Vue作为参数传入。执行后经过如下处理过程:
- 判断是否已经安装过,如果已经安装过,则不重复安装
- 将传入的
Vue构造函数赋值给全局的Vue变量,从而令Vue构造函数和Vuex关联,后续创建Store时创建Vue实例使用该构造函数 - 在Vue2.0以上的版本中,混入
beforeCreate钩子函数,在Vue2.0以下的版本使用重写Vue.prototype._init方法的方式,在初始化Vue实例时将Vuex的Store实例注入到所有的实例中。从而实现根应用下所有的子组件都可以访问到Store实例。
# 2. Store实例
Store类是Vuex的核心,Vuex所有的状态管理都是基于Store实例。Store实例中包含了state、getters、mutations、actions、modules等属性和方法。在初始化一个Store实例时,会执行Store类的构造函数new Store(options)。
options属性如下:
state:状态对象,默认为空对象actions:action对象,默认为空对象mutations:mutations对象,默认为空对象getters:getters对象,默认为空对象modules:模块对象,默认为空对象plugins:插件列表,默认为空数组strict:是否为严格模式,默认为false
执行过程如下:
- 使用script引入时会自动安装
Vuex - 通过
options.state,获取state的值,options.state可以是函数,函数返回值作为state的值;state的默认值是空对象 - 初始化各种属性:
_committing:一个布尔值,用于判断当前是否处于mutation中,默认为false_committing:是否正在提交,默认值为false,当该值被标记为true时,则state允许被修改。_actions:存放所有action的对象,默认值为空对象。_actionSubscribers:存放action订阅者,默认值为空数组,在使用dispatch派发action时,会遍历并触发订阅者_mutations:存放所有mutation的对象,默认值为空对象。_wrappedGetters:存放所有getters的包装函数的对象,默认值为空对象。_modules:根模块树,是Vuex模块化支持的基础,使用ModuleCollection类实例化,是具有一个root属性的模块树_modulesNamespaceMap:模块命名空间映射,定义的Vuex模块都会被映射到该对象的属性中,辅助函数的模块会从该对象获取。_subscribers:mutation订阅列表,默认值为空数组,在使用commit提交mutation时,会遍历该数组并执行订阅者_watcherVM:一个Vue实例,用于触发watcher,是store.watchAPI实现的基础strict:是否开启严格模式,从options中获取,默认值为false。在严格模式下,state只能通过mutation修改
- 执行
installModule(this, state, [], this._modules.root)安装根模块:- 带命名空间的模块存入
_modulesNamespaceMap对象中 - 将
state转为嵌套的对象 - 创建局部上下文,返回一个包含
dispatch,commit,getters,state的对象 - 注册
mutations到_mutations,有命名空间的键名会带上命名空间,同名的mutation其处理函数存放在同一个数组里,处理函数只接收局部上文的state - 注册
actions到_actions,有命名空间的键名会带上命名空间,同名的action其处理函数存放在同一个数组里,处理函数接收局部上下文和根上下文的state和getters作为参数 - 注册
getters,将getters包装后存到_wrappedGetters对象中,依次接收局部上下文的state、getters和根局部上下文的state、getters - 遍历子模块,递归安装子模块
- 带命名空间的模块存入
- 定义响应式数据
- 创建
_vm属性,其值为Vue实例,以state作为data.$$state选项传入,包装后的getters作为computed选项 - 将
store.getters代理至_vm实例的同名属性(计算属性) - 严格模式设置
- 销毁旧的实例
- 创建
- 安装插件
实例化后的store还具有一套API供外部调用,这些API如下:
dispatch(type, payload):派发action,返回一个Promise对象commit(type, payload, options):提交mutation,返回一个Promise对象replaceState(state):替换根stateregisterModule(path, module, options):注册动态模块unregisterModule(path):注销动态模块hasModule(path):判断是否已经注册同名的模块watch(getter, cb, options):监听getter,返回一个watcher对象subscribe(fn):订阅mutation,返回一个取消订阅的函数subscribeAction(fn):订阅action,返回一个取消订阅的函数hotUpdate(newOptions):热更新
# 3. 辅助方法
Vuex为我们提供了几个辅助函数,便于我们在项目中使用store实例的状态数据。函数列表如下:
mapState: 映射store的state属性到组件的computed属性中。mapGetters:映射store的getters属性到组件的computed属性中。mapActions:映射store的actions属性到组件的methods属性中。mapMutations:映射store的mutations属性到组件的methods属性中。createNamespacedHelpers:创建一个带命名空间的辅助函数,它们在使用前都已经绑定在了给定的命名空间。
# 4. 插件系统
Vuex提供插件机制来扩展其功功能,插件是一个函数,接收一个store实例作为参数。并且内置了devtoolPlugin插件和Logger插件。
Vuex完整的架构图如下:

# 二、常见问题
# 1. 如果在mutation中执行异步操作会怎么样?
在严格模式下会提示不在mutation外部修改state。这是因为,执行mutation时会临时将_committing设置为true,执行完成后会同步将_committing恢复为原来的值。如果是个异步操作,此时_committing的值为false,所以会提示。虽然给出了提示,但是state的状态其实已经变化了,所以仍然会触发watcher,页面使用到了state依然会更新。
# 2. 如果不使用mutation直接修改state会怎样?
在严格模式下会提示不在mutation外部修改state,同理state的状态其实已经变化了,所以仍然会触发watcher,页面使用到了state依然会更新。
# 3. 辅助函数是如何通过命名空间查找到对应模块的state、getters、mutation和action的?
辅助函数会根据传入的命名空间,从store._modulesNamespaceMap找到对应的模块,每一个模块都有一个context属性,该属性是一个包含state、getters、commit、dispatch的局部上下文对象,从中可以获取以上的属性。而getters是还可以通过命名空间直接从store.getters获取,Vuex内部采用的第二种方式。