Table of Contents
版本:3.4.0
index.js
此文件导出 vuex 对象。
export default { Store, // Store 类 install, // 安装钩子,vue.use() 调用 version: '__VERSION__', // 版本 mapState, // 辅助函数,下同 mapMutations, mapGetters, mapActions, createNamespacedHelpers }
store.js#install
// store.js // 开头,第六行 let Vue // bind on install …… // 最后 export function install (_Vue) { if (Vue && _Vue === Vue) { if (__DEV__) { console.error( '[vuex] already installed. Vue.use(Vuex) should be called only once.' ) } return } Vue = _Vue applyMixin(Vue) }
如果已经注册了,就不再注册。 applyMixin 在 mixin.js 里。其会调用 vuexInit 把 store 实例挂载到每个 vue 实例上。这样,就可以通过 this.$store 调用了。
function vuexInit () { const options = this.$options // store injection 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 } }
如果 vue 实例有 store,说明是根节点,使用根节点的 store,否则使用父节点的。
// 根节点配置 new Vue({ el: '#app', router, store, components: { App }, template: '<App/>' })
store.js#Store
vuex 模块是嵌套结构,实例化的时候,vuex 会根据 options 进行递归挂载。
this._modules = new ModuleCollection(options)
在 module/module-collection.js 里的 register 方法会进行递归注册挂载。
export default class ModuleCollection { constructor (rawRootModule) { // register root module (Vuex.Store options) this.register([], rawRootModule, false) // 第三个参数暂不明 } get (path) { return path.reduce((module, key) => { return module.getChild(key) }, this.root) } register (path, rawModule, runtime = true) { const newModule = new Module(rawModule, runtime) if (path.length === 0) { // 根节点 this.root = newModule } else { // 子模块 const parent = this.get(path.slice(0, -1)) // 这里的 get 是用来获取 path 指定模块的 parent.addChild(path[path.length - 1], newModule) } // register nested modules if (rawModule.modules) { // 这里是递归注册 forEachValue(rawModule.modules, (rawChildModule, key) => { this.register(path.concat(key), rawChildModule, runtime) }) } } }
installModule
在 installModule 函数里,mutation、action、getter 会被注册到模块上,同时递归处理子模块。
resetStoreVM
在这个函数里会对数据进行响应式处理。
store._vm = new Vue({ data: { $$state: state }, computed })
Store.commit & Store.dispatch
Store 的 commit 和 dispatch 都会先进行参数校验和转换。因为 commit 和 dispatch 都有两种传参方式。
commit(type: string, payload?: any, options?: Object) commit(mutation: Object, options?: Object) dispatch(type: string, payload?: any, options?: Object): Promise<any> dispatch(action: Object, options?: Object): Promise<any>
commit 在执行 mutation 的时候,会通过 this._withCommit 设置 this._committing 这个状态。这是为了在严格模式下能够给出非 mutation 更新数据的警告。
function enableStrictMode (store) { store._vm.$watch(function () { return this._data.$$state }, () => { if (__DEV__) { assert(store._committing, `do not mutate vuex store state outside mutation handlers.`) } }, { deep: true, sync: true }) }
vuex 将指定类型的 mutation 和 action 都视为数组,mutation 是同步,而 action 是异步。
commit (_type, _payload, _options) { // check object-style commit const { type, payload, options } = unifyObjectStyle(_type, _payload, _options) const mutation = { type, payload } const entry = this._mutations[type] this._withCommit(() => { entry.forEach(function commitIterator (handler) { handler(payload) }) }) } dispatch (_type, _payload) { // check object-style dispatch const { type, payload } = unifyObjectStyle(_type, _payload) const action = { type, payload } const entry = this._actions[type] const result = entry.length > 1 ? Promise.all(entry.map(handler => handler(payload))) : entry[0](payload) return new Promise((resolve, reject) => { result.then(res => { resolve(res) }, error => { reject(error) }) }) }