Open jyzwf opened 6 years ago
这是对 我看Vuex(二) 里面的一些函数的介绍,感觉放在二里面,层次显得不清楚
// module/module-collection.js getNamespace(path) { // 当根模块到前模块的路径 let module = this.root return path.reduce((namespace, key) => { module = module.getChild(key) // 如果模块设置了命名空间,就取从根模块到该模块的key值作为命名空间 // 上面 moduleC 如果设置了命名空间,那么 moduleC 的 namespace 为 “moduleA/moduleC” // 这里为什么不直接调用模块的 _rawModule.namespaced 呢???? return namespace + (module.namespaced ? key + '/' : '') }, '') }
这里注意一下,这个module 是一个模块实例,所以当他获取 namespace 属性时是调用下面的代码,还有一点就是 根模块的 namespace 是 空字符串
空字符串
// module.js get namespaced () { // _rawModule 保存了模块的对象描述 return !!this._rawModule.namespaced }
function makeLocalContext(store, namespace, path) { const noNamespace = namespace === '' // 判断是否有命名空间 // 这里是在本地dispatch / commit // 如下面例子: /* actions: { actionA(){}, actionB({ dispatch, commit }) { return dispatch('actionA').then(() => { commit('someOtherMutation') }) } */ // 所以说,如果在组件实例中 dispatch ,就要加上模块名 const local = { // 如果没有命名空间,就直接获取 store的dispatch dispatch: noNamespace ? store.dispatch : (_type, _payload, _options) => { // unifyObjectStyle :获取正确的 type / payload /options // Actions 支持同样的载荷方式和对象方式进行分发: const args = unifyObjectStyle(_type, _payload, _options) const { payload, options } = args let { type } = args // options 不存在,或者其root 属性不存在 // 因为vuex 支持下面功能: // 需要在全局命名空间内分发 action 或提交 mutation, // 将 { root: true } 作为第三参数传给 dispatch 或 commit 即可 // 这里是不需要分发的情况 if (!options || !options.root) { type = namespace + type // 获取命名空间下的 action // 使用命名空间 if (process.env.NODE_ENV !== 'production' && !store._actions[type]) { // 确保 dispatch 的 action存在 console.error(`[vuex] unknown local action type: ${args.type}, global type: ${type}`) return } } // 这里已经对type 做了命名空间的处理, // 如果传入了 { root: true } ,这里的 type 就是没有加上 namespace 的, // 如果没有传入 { root: true },这里的 type 经过上面的 if 处理,也就可以正确触发了 return store.dispatch(type, payload) }, commit: noNamespace ? store.commit : (_type, _payload, _options) => { // 和上面差不多,,不讲了,, } } // getters and state object must be gotten lazily // because they will be changed by vm update Object.defineProperties(local, { getters: { get: noNamespace ? () => store.getters : // 没有,直接获取getters () => makeLocalGetters(store, namespace) // 有,获取自己本模块的 getters }, state: { // 获取嵌套的state get: () => getNestedState(store.state, path) } }) return local }
unifyObjectStyle
function makeLocalGetters(store, namespace) { const gettersProxy = {} // 获取命名空间长度 const splitPos = namespace.length // 循环每个 getters Object.keys(store.getters).forEach(type => { // skip if the target getter is not match this namespace // 跳过没有匹配与命名空间不匹配的 getter if (type.slice(0, splitPos) !== namespace) return // extract local getter type // 提出 getter 类型 // 有 namespace 如:namespace 为 moduleA/moduleC/,type 为 moduleA/moduleC/cGetter,则 localType 为 cGetter // 没有 namespace , 或者说那么spacename ='' ,type 为 cGetter,则 localType 为 cGetter // slice 不改变原字符串 const localType = type.slice(splitPos) // Add a port to the getters proxy. // Define as getter property because // we do not want to evaluate the getters in this time. Object.defineProperty(gettersProxy, localType, { get: () => store.getters[type], // 获取原字符串所对应的 getter enumerable: true }) }) return gettersProxy }
// 注册各个模块的 mutaations 方法到 store._mutations 中,每个type对应一个数组 function registerMutation(store, type, handler, local) { // 将相同的type放入到同一个数组中,这是因为在没有命名空间的情况下,各个模块会有相同的 mutation // 这样把这些 mutations 注册到全局,commit(type) 时候,就全部触发 const entry = store._mutations[type] || (store._mutations[type] = []) entry.push(function wrappedMutationHandler(payload) { // 获取负载,只有它是需要用户传进来的 handler.call(store, local.state, payload) }) }
// 注册各个模块的 actions 到store._actions function registerAction(store, type, handler, local) { const entry = store._actions[type] || (store._actions[type] = []) // 这里要说明下,由于 action 里是执行异步的地方,所以,如果没有命名空间的情况下 // 多个相同 type 的action 加入到 store._actions[type],dispatch(type) 的时候, // 就要等这几个 action 都完成后才能 返回结果,所以这里用来 Promise 来处理,于此同时, // 在dispatch(type) 里面,也会使用 Promise.all(),来等待所有结果返回 entry.push(function wrappedActionHandler(payload, cb) { let res = handler.call(store, { dispatch: local.dispatch, commit: local.commit, getters: local.getters, state: local.state, rootGetters: store.getters, rootState: store.state }, payload, cb) if (!isPromise(res)) { res = Promise.resolve(res) } // 调用开发者工具,并在错误的时候触发 vuex:error if (store._devtoolHook) { return res.catch(err => { store._devtoolHook.emit('vuex:error', err) throw err }) } else { return res } }) }
// 注册各个模块的 getters 到store._wrappedGetters function registerGetter(store, type, rawGetter, local) { // getter 不能重复,因为他是依靠 vue 的computed 属性,computed 属性不能重复,这个也就不能重复 if (store._wrappedGetters[type]) { if (process.env.NODE_ENV !== 'production') { console.error(`[vuex] duplicate getter key: ${type}`) } return } store._wrappedGetters[type] = function wrappedGetter(store) { return rawGetter( local.state, // local state local.getters, // local getters store.state, // root state store.getters // root getters ) } }
在上面和前面有不少地方调用了 _withCommit 函数,,这个函数他有啥妙用呢? 他就是防止 state 被除了 mutation 以外的函数或者直接被修改,这个函数在 store 类里面,要结合 enableStrictMode 函数来看
_withCommit
他就是防止 state 被除了 mutation 以外的函数或者直接被修改
store
enableStrictMode
_withCommit(fn) { const committing = this._committing this._committing = true fn() this._committing = committing } function enableStrictMode(store) { store._vm.$watch(function () { return this._data.$$state }, () => { if (process.env.NODE_ENV !== 'production') { assert(store._committing, `Do not mutate vuex store state outside mutation handlers.`) } }, { deep: true, sync: true }) }
从上面函数知道,它只是改变 this._commiting 的状态,在 enableStrictMode 里监听 state。我们知道 vuex 规定只有在 mutation 函数才能修改 state,这里当 _withCommit 执行时,会执行 传入的 fn 函数,然后 fn 修改了 state ,导致 enableStrictMode 里的 watch检测到了变化,执行后面的函数,在里面先进行判断 this._commiting 是否是 true,否则就会抛出错误,这样就做到了很好的保证
this._commiting
state
mutation
fn
watch
true
注:enableStrictMode 函数只有在 strict = true,才会执行,所以开发环境下,最好开启
strict = true
总的来说就是把所有的 getter,mutation,action 放到 store 上,然后做相应的操作
getter
action
这是对 我看Vuex(二) 里面的一些函数的介绍,感觉放在二里面,层次显得不清楚
getNamespace
makeLocalContext
unifyObjectStyle
makeLocalGetters
registerMutation
registerAction
registerGetter
在上面和前面有不少地方调用了
_withCommit
函数,,这个函数他有啥妙用呢?他就是防止 state 被除了 mutation 以外的函数或者直接被修改
,这个函数在store
类里面,要结合enableStrictMode
函数来看从上面函数知道,它只是改变
this._commiting
的状态,在enableStrictMode
里监听state
。我们知道 vuex 规定只有在mutation
函数才能修改state
,这里当_withCommit
执行时,会执行 传入的fn
函数,然后fn
修改了state
,导致enableStrictMode
里的watch
检测到了变化,执行后面的函数,在里面先进行判断this._commiting
是否是true
,否则就会抛出错误,这样就做到了很好的保证总结
总的来说就是把所有的
getter
,mutation
,action
放到store
上,然后做相应的操作