在使用vuex的时,我们实际上是实例化Store类,然后传入一个对象,这个对象包括我们定义好的state、getters、mutations、actions等,当然大点的项目还会有modules。
这一节我们就来看看Store类在实例化的时候做了哪些事情,首先看下构造函数的代码。
constructor (options = {}) {
// Auto install if it is not done yet and `window` has `Vue`.
// To allow users to avoid auto-installation in some cases,
// this code should be placed here. See #731
if (!Vue && typeof window !== 'undefined' && window.Vue) {
//通过浏览器script标签使用vuex时自动安装
install(window.Vue)
}
if (process.env.NODE_ENV !== 'production') {
//断言一些使用环境,必须使用vue.use(vuex),必须存在Promise,必须使用new操作符
assert(Vue, `must call Vue.use(Vuex) before creating a store instance.`)
assert(typeof Promise !== 'undefined', `vuex requires a Promise polyfill in this browser.`)
assert(this instanceof Store, `store must be called with the new operator.`)
}
const {
plugins = [],
strict = false
} = options
// store internal state
//初始化需要使用到的变量
this._committing = false
this._actions = Object.create(null)
this._actionSubscribers = []
this._mutations = Object.create(null)
this._wrappedGetters = Object.create(null)
this._modules = new ModuleCollection(options) //这里会对模块module进行收集
this._modulesNamespaceMap = Object.create(null)
this._subscribers = []
this._watcherVM = new Vue()
// bind commit and dispatch to self
//将dispatch和commit方法的this指针绑定到store上,防止被修改
const store = this
const { dispatch, commit } = this
this.dispatch = function boundDispatch (type, payload) {
return dispatch.call(store, type, payload)
}
this.commit = function boundCommit (type, payload, options) {
return commit.call(store, type, payload, options)
}
// strict mode
this.strict = strict
const state = this._modules.root.state
// init root module.
// this also recursively registers all sub-modules
// and collects all module getters inside this._wrappedGetters
//处理的核心,包括处理根module、action、mutation、getters和递归注册子module
installModule(this, state, [], this._modules.root)
// initialize the store vm, which is responsible for the reactivity
// (also registers _wrappedGetters as computed properties)
//也是重点,使用vue实例来保存state和getter
resetStoreVM(this, state)
// apply plugins
//插件注册和使用,内置的有devtool和logger
plugins.forEach(plugin => plugin(this))
const useDevtools = options.devtools !== undefined ? options.devtools : Vue.config.devtools
if (useDevtools) {
devtoolPlugin(this)
}
}
我们来简要理一理这个构造过程,先是使用assert断言一下运行环境,然后初始化需要用到的变量,再就是模块安装installModule和组件初始化resetStoreVM,最后注册和使用插件。下面我们一一展开。
先是assert断言运行环境,这是一种很好的写法。assert函数在util.js文件中。这里断言必须先调用Vue.use(Vuex)。必须提供Promise,应该是为了让Vuex的体积更小,让开发者自行提供Promise的polyfill,一般我们可以使用babel-runtime或者babel-polyfill引入。最后断言必须使用new操作符调用Store函数。
assert(Vue, `must call Vue.use(Vuex) before creating a store instance.`)
assert(typeof Promise !== 'undefined', `vuex requires a Promise polyfill in this browser.`)
assert(this instanceof Store, `store must be called with the new operator.`)
然后是一些内部变量的初始化,下面我们先对这些变量的作用进行下说明便于后面的理解。
this._committing = false
this._actions = Object.create(null)
this._actionSubscribers = []
this._mutations = Object.create(null)
this._wrappedGetters = Object.create(null)
this._modules = new ModuleCollection(options) //这里会对模块module进行收集
this._modulesNamespaceMap = Object.create(null)
this._subscribers = []
this._watcherVM = new Vue()
_committing提交状态的标志,在_withCommit中,当使用mutation时,会先赋值为true,再执行mutation,修改state后再赋值为false, 在这个过程中,会用watch监听state的变化时是否_committing为true,从而保证只能通过mutation来修改state
_actions用于保存所有action,里面会先包装一次 _actionSubscribers用于保存订阅action的回调 _mutations用于保存所有的mutation,里面会先包装一次 _wrappedGetters用于保存包装后的getter _modules用于保存一棵module树 _modulesNamespaceMap用于保存namespaced的模块 _subscribers用于存储所有对 mutation 变化的订阅者 _watcherVM Vue对象的实例,主要是利用 Vue 实例方法 $watch 来观测变化的
这里提一下this._modules = new ModuleCollection(options)对模块module进行收集的逻辑。实际上是调用了module目录下的ModuleCollection类和Module类。
//module目录下的moduleCollection.js
export default class ModuleCollection {
constructor (rawRootModule) {
// register root module (Vuex.Store options)
this.register([], rawRootModule, false)
}
/** 省略 **/
}
//module目录下的module.js
export default class Module {
constructor (rawModule, runtime) {
this.runtime = runtime
// Store some children item
this._children = Object.create(null)
// Store the origin module object which passed by programmer
this._rawModule = rawModule
const rawState = rawModule.state
// Store the origin module's state
this.state = (typeof rawState === 'function' ? rawState() : rawState) || {}
}
/** 省略 **/
}
这里调用ModuleCollection构造函数,首先进行根module的注册,然后递归遍历所有的module,子module 添加到父module的_children属性上,最终形成一棵树。
再就是模块安装installModule函数,这里是module的处理核心,包括处理根module、命名空间、action、mutation、getters和递归注册子module。
function installModule (store, rootState, path, module, hot) {
const isRoot = !path.length
const namespace = store._modules.getNamespace(path)
// register in namespace map
//保存namespaced模块
if (module.namespaced) {
store._modulesNamespaceMap[namespace] = module
}
// set state
//非根组件设置state,根据path获取父state
if (!isRoot && !hot) {
const parentState = getNestedState(rootState, path.slice(0, -1))
const moduleName = path[path.length - 1]
store._withCommit(() => {
Vue.set(parentState, moduleName, module.state)
})
}
//设置module的上下文,从而保证mutation和action的第一个参数能拿到对应的state getter等
const local = module.context = makeLocalContext(store, namespace, path)
//逐一注册mutation
module.forEachMutation((mutation, key) => {
const namespacedType = namespace + key
registerMutation(store, namespacedType, mutation, local)
})
//逐一注册action
module.forEachAction((action, key) => {
const type = action.root ? key : namespace + key
const handler = action.handler || action
registerAction(store, type, handler, local)
})
//逐一注册getter
module.forEachGetter((getter, key) => {
const namespacedType = namespace + key
registerGetter(store, namespacedType, getter, local)
})
//逐一注册子module
module.forEachChild((child, key) => {
installModule(store, rootState, path.concat(key), child, hot)
})
}