一、命名空间使用
在子模块对象中添加 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/c
return path.reduce((namespaceStr, key) => {
module = module.getChild(key); // 子模块
return namespaceStr + (module.namespaced ? key + '/' : '')
}, '')
}
3、实现getter、mutation、action的命名空间
以getter为例,拼接时属性名前面加上namespaced
store._wrappedGetters[namespaced + key] = () => {
// 通过这个方法去获取最新的state
return 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 功能,即:根模块与各子模块中定义的事件完全独立互不影响;