总结与常见问题
到此我们已经将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.watch
API实现的基础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)
:替换根state
registerModule(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
内部采用的第二种方式。