一、命名空间使用
在子模块对象中添加 namespaced:true,为模块开启命名空间功能;
开启命名空间功能,相当于为每个模块添加独立的作用域,实现模块间状态和事件的隔离;
二、命名空间实现逻辑
在模块注册阶段,会通过类似发布订阅的方式将各模块中的 action、mutation 进行收集并注册,需要根据模块是否开启命名空间状态,为模块拼接命名空间前缀;
所以,可以统一理解为,在事件订阅时,为事件添加对应命名空间标识即可;
三、命名空间实现
1、命名空间标识
在模块收集注册时,添加是否开启命名空间标识
export default class Module {constructor(rawModule) {...this.namespaced = rawModule.namespaced; // 自己是否有命名空间}
}
2、拿到namespace
installModule
安装时需要用到namespace
,在installModule
中调用方法获取命名空间
function installModule(store, rootState, path, module) {// 递归安装let isRoot = !path.length; // 如果数组是空数组 说明是根,否则不是// 下面需要使用namespace,在这里把命名空间进行处理const namespaced = store._modules.getNamespaced(path); // [a,c]console.log('--',path);console.log(namespaced);}
_module
是ModuleCollection
生成的,在ModuleCollection
定义getNamespaced
方法
getNamespaced(path) {let module = this.root // [a,c] a/creturn path.reduce((namespaceStr, key) => {module = module.getChild(key); // 子模块return namespaceStr + (module.namespaced ? key + '/' : '')}, '')}
3、实现getter、mutation、action的命名空间
以getter为例,拼接时属性名前面加上namespaced
store._wrappedGetters[namespaced + key] = () => {// 通过这个方法去获取最新的statereturn getter(getNestedState(store.state, path));};
四、测试
a模块-------------------{{ aCount }}<br>b模块-------------------{{ bCount }}<br>c模块-------------------{{ cCount }}<br><button @click="$store.commit('aCount/add',1)">change a</button><button @click="$store.commit('bCount/add',1)">change b</button><button @click="$store.commit('aCount/cCount/add',1)">change c</button>
五、核心逻辑梳理
- 在 ModuleCollection 模块收集类中,提供根据 path 获取命名空间标识的能力:getNamespaced(path);
- 在 installModule 模块安装时,通过调用getNamespaced(path) 获取当前模块的命名空间标识;
- 在安装/注册mutation、action、getter 时,为对应的事件添加(拼接)上命名空间标识;
这样,就实现了 Vuex 命名空间 namespaced 功能,即:根模块与各子模块中定义的事件完全独立互不影响;