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)
})
})
}