手写vuex4源码(六)命名空间实现

一、命名空间使用

在子模块对象中添加 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);
  }

_moduleModuleCollection生成的,在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 + '/' : '')
        }, '')
    }

手写vuex4源码(六)命名空间实现_第1张图片

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>

手写vuex4源码(六)命名空间实现_第2张图片

五、核心逻辑梳理

  • 在 ModuleCollection 模块收集类中,提供根据 path 获取命名空间标识的能力:getNamespaced(path);
  • 在 installModule 模块安装时,通过调用getNamespaced(path) 获取当前模块的命名空间标识;
  • 在安装/注册mutation、action、getter 时,为对应的事件添加(拼接)上命名空间标识;
    这样,就实现了 Vuex 命名空间 namespaced 功能,即:根模块与各子模块中定义的事件完全独立互不影响;

你可能感兴趣的:(手写vuex4.x源码,javascript,前端,vue.js)