我们都知道Vuex提供了
4个概念
- state
- mutation
- action
- module
3个API
- commit
- dispatch
- registerModule
他们的实现都在src/store.js里
export class Store {
import applyMixin from './mixin'
import devtoolPlugin from './plugins/devtool'
import ModuleCollection from './module/module-collection'
import { forEachValue, isObject, isPromise, assert, partial } from './util'
let Vue // bind on install
export class 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) {
install(window.Vue)
}
if (process.env.NODE_ENV !== 'production') {
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)//把模块关系树整理好
console.log('this._modules', this._modules)
this._modulesNamespaceMap = Object.create(null)
this._subscribers = []
this._watcherVM = new Vue()
this._makeLocalGettersCache = Object.create(null)
// bind commit and dispatch to self
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
installModule(this, state, [], this._modules.root)
// initialize the store vm, which is responsible for the reactivity
// (also registers _wrappedGetters as computed properties)
resetStoreVM(this, state)
// apply plugins
plugins.forEach(plugin => plugin(this))
const useDevtools = options.devtools !== undefined ? options.devtools : Vue.config.devtools
if (useDevtools) {
devtoolPlugin(this)
}
}
get state () {
return this._vm._data.$$state
}
set state (v) {
if (process.env.NODE_ENV !== 'production') {
assert(false, `use store.replaceState() to explicit replace store state.`)
}
}
}
这里的那个installModule实现了getters,mutations, actions的实现
由于register在上篇文章初始化的时候已经讲过了,而mutations就是根据现有上下文调用下传进来的方法,没什么好说的,这里重点看下actions:
function registerAction (store, type, handler, local) {
const entry = store._actions[type] || (store._actions[type] = [])
entry.push(function wrappedActionHandler (payload) {
let res = handler.call(store, {
dispatch: local.dispatch,
commit: local.commit,
getters: local.getters,
state: local.state,
rootGetters: store.getters,
rootState: store.state
}, payload)
if (!isPromise(res)) {//不是promsie对象就返回一个promise对象
res = Promise.resolve(res)
}
if (store._devtoolHook) {
return res.catch(err => {
store._devtoolHook.emit('vuex:error', err)
throw err
})
} else {
return res
}
})
}
可以看到如果我么平常不在action里传promise对象,那么它也是会将我们的传入的参数通过Promise.resovle转化为Promise对象的。
再来看dispatch
dispatch (_type, _payload) {
// check object-style dispatch
const {
type,
payload
} = unifyObjectStyle(_type, _payload)
const action = { type, payload }
const entry = this._actions[type]
if (!entry) {
if (process.env.NODE_ENV !== 'production') {
console.error(`[vuex] unknown action type: ${type}`)
}
return
}
try {
this._actionSubscribers
.slice() // shallow copy to prevent iterator invalidation if subscriber synchronously calls unsubscribe
.filter(sub => sub.before)
.forEach(sub => sub.before(action, this.state))
} catch (e) {
if (process.env.NODE_ENV !== 'production') {
console.warn(`[vuex] error in before action subscribers: `)
console.error(e)
}
}
const result = entry.length > 1
? Promise.all(entry.map(handler => handler(payload)))
: entry[0](payload)
return result.then(res => {
try {
this._actionSubscribers
.filter(sub => sub.after)
.forEach(sub => sub.after(action, this.state))
} catch (e) {
if (process.env.NODE_ENV !== 'production') {
console.warn(`[vuex] error in after action subscribers: `)
console.error(e)
}
}
return res
})
}
可以看到通过Promise.all只有当所有操作都成功了才会resolve的。
再来看registerModule
registerModule (path, rawModule, options = {}) {
if (typeof path === 'string') path = [path]
if (process.env.NODE_ENV !== 'production') {
assert(Array.isArray(path), `module path must be a string or an Array.`)
assert(path.length > 0, 'cannot register the root module by using registerModule.')
}
this._modules.register(path, rawModule)//这个方法在ModellCollection类中定义的
installModule(this, this.state, path, this._modules.get(path), options.preserveState)
// reset store to update getters...
resetStoreVM(this, this.state)
}
registerModule通过register方法去扩展模块,通过installModule去安装模块,然后通过resetStoreVM重新生成了Stroe._vm.最终达到了动态生成module的目的。
ps: 这就是为什么state/getter需要动态实时获取的原因之一。
总共的原因有:
- 通过store.registerModule注册动态模块时,会增加部分state/getter
- 通过store.unregisterModule卸载动态模块时,会删除部分state/getter
- 通过store.replaceState(常见的使用场景是 SSR)替换掉整个state
总结:
1. action 返回的是一个Promse
2. dispatch通过Promise.all去处理多个action的情况
3. registerModule通过以下3步去动态添加模块
- 扩展模块(ModuleCollection的register方法),
- 安装模块(installModule)
- 重新生成Store中的Vue实例(store._vm)
网友评论