if (__DEV__) { // 必须使用 Vue.use(Vuex) 创建store实例 assert(Vue, `must call Vue.use(Vuex) before creating a store instance.`) // 当前浏览器不支持 promise,需要使用 polyfill assert(typeofPromise !== 'undefined', `vuex requires a Promise polyfill in this browser.`) // Store 需要用 new 来实例化 assert(thisinstanceofStore, `store must be called with the new operator.`) }
// 绑定 dispatch 和 commit 的 this 到 store 对象,确保在这两个方法中的this指向store实例 const store = this const { dispatch, commit } = this this.dispatch = functionboundDispatch (type, payload) { return dispatch.call(store, type, payload) } this.commit = functionboundCommit (type, payload, options) { return commit.call(store, type, payload, options) }
// 严格模式 this.strict = strict // 根模块的 state const state = this._modules.root.state
// init root module. 初始化根模块 // this also recursively registers all sub-modules 同时递归注册所有子模块 // and collects all module getters inside this._wrappedGetters // 并将模块中的 getters 收集到 _wrappedGetters 里 installModule(this, state, [], this._modules.root)
// initialize the store vm, which is responsible for the reactivity // (also registers _wrappedGetters as computed properties) // 初始化 store vm 实例以提供响应式,同时将 _wrappedGetters 作为 computed 属性注册 resetStoreVM(this, state)
// register in namespace map // 模块命名空间在map中已经有了 if (module.namespaced) { if (store._modulesNamespaceMap[namespace] && __DEV__) { console.error(`[vuex] duplicate namespace ${namespace} for the namespaced module ${path.join('/')}`) } store._modulesNamespaceMap[namespace] = module } // 分别对state、mutation、action、getter进行注册 // set state if (!isRoot && !hot) { const parentState = getNestedState(rootState, path.slice(0, -1)) const moduleName = path[path.length - 1] store._withCommit(() => { if (__DEV__) { // 开发环境会报重名错误 if (moduleName in parentState) { console.warn( `[vuex] state field "${moduleName}" was overridden by a module with the same name at "${path.join('.')}"` ) } } Vue.set(parentState, moduleName, module.state) }) }
const local = module.context = makeLocalContext(store, namespace, path)
// getters and state object must be gotten lazily // because they will be changed by vm update // 由于getters 和 state 是响应式的,在虚拟dom更新的时候进行改变,因此需要懒加载(延迟获得??不太会翻译) Object.defineProperties(local, { getters: { get: noNamespace ? () => store.getters : () =>makeLocalGetters(store, namespace) }, state: { get: () =>getNestedState(store.state, path) } })
// bind store public getters store.getters = {} // reset local getters cache 重置getters缓存 store._makeLocalGettersCache = Object.create(null) const wrappedGetters = store._wrappedGetters const computed = {} forEachValue(wrappedGetters, (fn, key) => { // use computed to leverage its lazy-caching mechanism // direct inline function use will lead to closure preserving oldVm. // using partial to return function with only arguments preserved in closure environment. computed[key] = partial(fn, store) Object.defineProperty(store.getters, key, { get: () => store._vm[key], enumerable: true// for local getters }) })
// use a Vue instance to store the state tree // suppress warnings just in case the user has added // some funky global mixins // 使用一个 Vue 实例对象存储 state 树,警告以防止用户添加的一些全局mixins const silent = Vue.config.silent Vue.config.silent = true store._vm = newVue({ data: { $$state: state }, computed }) Vue.config.silent = silent
// enable strict mode for new vm if (store.strict) { enableStrictMode(store) }
if (oldVm) { if (hot) { // dispatch changes in all subscribed watchers // to force getter re-evaluation for hot reloading. store._withCommit(() => { oldVm._data.$$state = null }) } Vue.nextTick(() => oldVm.$destroy()) } }
get state () { returnthis._vm._data.$$state } set state (v) { if (__DEV__) { assert(false, `use store.replaceState() to explicit replace store state.`) } }
exportconst mapState = normalizeNamespace((namespace, states) => { const res = {} // 用于接收map之后的state属性 if (__DEV__ && !isValidMap(states)) { console.error('[vuex] mapState: mapper parameter must be either an Array or an Object') } normalizeMap(states).forEach(({ key, val }) => { res[key] = functionmappedState () { let state = this.$store.state let getters = this.$store.getters if (namespace) { constmodule = getModuleByNamespace(this.$store, 'mapState', namespace) if (!module) { return } state = module.context.state getters = module.context.getters } returntypeof val === 'function' ? val.call(this, state, getters) : state[val] } // mark vuex getter for devtools res[key].vuex = true }) return res })
之后遍历转换后states对象,每个新对象每个元素都返回一个新的函数 mappedState,函数对val的类型判断,如果 val 是一个函数,则直接调用这个 val 函数,把当前 store 上的state和 getters 作为参数,返回值作为 mappedState 的返回值;否则直接把this.$store.state[val]作为 mappedState 的返回值。
exportconst mapGetters = normalizeNamespace((namespace, getters) => { const res = {} if (__DEV__ && !isValidMap(getters)) { console.error('[vuex] mapGetters: mapper parameter must be either an Array or an Object') } normalizeMap(getters).forEach(({ key, val }) => { // The namespace has been mutated by normalizeNamespace val = namespace + val res[key] = functionmappedGetter () { if (namespace && !getModuleByNamespace(this.$store, 'mapGetters', namespace)) { return } if (__DEV__ && !(val inthis.$store.getters)) { console.error(`[vuex] unknown getter: ${val}`) return } returnthis.$store.getters[val] } // mark vuex getter for devtools res[key].vuex = true }) return res })
mapGetters 方法会将 store 中的 getter 映射到局部计算属性中。它的实现也和 mapState 类似,区别在于它的 val 只能是字符串,而且会检查 val in this.$store.getters 的值,如果为 false 会报错
exportconst mapMutations = normalizeNamespace((namespace, mutations) => { const res = {} if (__DEV__ && !isValidMap(mutations)) { console.error('[vuex] mapMutations: mapper parameter must be either an Array or an Object') } normalizeMap(mutations).forEach(({ key, val }) => { res[key] = functionmappedMutation (...args) { // Get the commit method from store let commit = this.$store.commit if (namespace) { constmodule = getModuleByNamespace(this.$store, 'mapMutations', namespace) if (!module) { return } commit = module.context.commit } returntypeof val === 'function' ? val.apply(this, [commit].concat(args)) : commit.apply(this.$store, [val].concat(args)) } }) return res })
mapMutations会将 store 中的 commit 方法映射到组件的 methods 中。
exportconst mapActions = normalizeNamespace((namespace, actions) => { const res = {} if (__DEV__ && !isValidMap(actions)) { console.error('[vuex] mapActions: mapper parameter must be either an Array or an Object') } normalizeMap(actions).forEach(({ key, val }) => { res[key] = functionmappedAction (...args) { // get dispatch function from store let dispatch = this.$store.dispatch if (namespace) { constmodule = getModuleByNamespace(this.$store, 'mapActions', namespace) if (!module) { return } dispatch = module.context.dispatch } returntypeof val === 'function' ? val.apply(this, [dispatch].concat(args)) : dispatch.apply(this.$store, [val].concat(args)) } }) return res })
mapActions的实现几乎和 mapMutations 一样,唯一差别就是映射的是 store 的 dispatch 方法。