vuex源码分析(四)——installModule

本文参考珠峰架构公开课之vuex实现原理

之前的文章谈到了module的数据的注册,接下来就是基于注册数据,执行installModule函数安装。

installModule函数的作用

先来看下平时vuex怎么调用这些模块:

//获取state
this.$store.state.state_country
//获取getters
this.$store.getters.getter_country
//设置mutations
this.$store.commit("mutation_country","中国")
//设置actions
this.$store.dispatch("action_country",{})

组件开发中是通过this.$store来操作stategettersmutationsactions,所以在Store类中存在stategetters两个属性,同时存在commitdispatch两个方法分别用来触发mutationaction,先看下Store类具体做了什么?

class Store {
  constructor(options) {
    this.vm = new Vue({
      data() {
        return {
          temp_data: options.state
        };
      }
    });
    //将options传入的数据进行格式化
    this.modules = new ModuleCollection(options);
    this.getters = {};
    this.mutations = {};
    this.actions = {};
    installModule(this, this.state, [], this.modules.root);
  }
  get state() {
    return this.vm.temp_data;
  }
  commit = (mutationName, payload) => {
    this.mutations[mutationName].forEach(fn => fn(mutationName, payload));
  };
  dispatch = (actionName, payload) => {
    this.actions[actionName].forEach(fn => fn(actionName, payload));
  };
}

Store类主要做了以下三件事:

  • 除了将options传入ModuleCollection函数做了数据格式化。
  • 以及通过实例化Vue对象将state数据作为Vue的属性实现双向绑定。
  • 还新增了gettersmutationsactions三个属性。然后通过installModule函数将各子模块的gettersmutationsactions整合到Store对应的属性上。

总结来说,installModule的作用就是通过递归将各个子模块上的stategettersmutationsactions整合到Store类的stategettersmutationsactions四个属性上,从而可以在组件中通过this.$store.xx来操作。

installModule函数的实现过程

installModule函数主要是安装stategettersmutationsactions,如果有子模块的话,重复之前的安装步骤,看下代码的实现:

//将格式化后的数据递归绑定到getters/mutations/actions
const installModule = (store, rootState, path, modules) => {
  //分别安装state,getters,mutations和actions
  installState(path, rootState, modules);
  installGetters(store, modules);
  installMutations(store, modules);
  installActions(store, modules);
  //如果有子模块的话
  if (Object.keys(modules._children).length > 0) {
    Object.keys(modules._children).forEach(childModuleName => {
      installModule(
        store,
        rootState,
        path.concat(childModuleName),
        modules._children[childModuleName]
      );
    });
  }
};

installModule函数的四个参数表示什么意思?

  • store:表示当前Store类。
  • rootState:表示当前要安装模块的state模块。
  • path:表示当前模块和所有父级模块的模块名的集合。在递归过程中,如果有子模块的话会将当前模块的模块名合并子模块的模块名作为下一个installModule函数中的path参数。举个例子:如果根模块有子模块citycity子模块又有district子模块,因为有三级,按层级划分就是根模块,city模块和district模块,那这个path在注册每个模块时处理结果如下:
    第一次对根模块执行installModulepath[]
    第二次对city模块执行installModulepath[ 'city' ]
    第三次对district模块执行installModulepath[ 'city' , 'district' ]
  • modules:表示当前要安装的模块。

installState实现过程

const installState = (path, rootState, modules) => {
  if (path.length > 0) {
    let parentState = path.slice(0, -1).reduce((root, current) => {
      return root[current];
    }, rootState);
    Vue.set(parentState, path[path.length - 1], modules.state);
  }
};

如果不是根模块,那就获取它父级模块的state,并在父级模块的state上添加一个新属性,属性名为当前的模块名,属性值为当前模块的state,最终store上的state参考如下结构:

{
  state_country: "中国",
  city: {
    state_city: "上海",
    district: {
      state_district: "黄浦区"
    }
  }
};

vuex中的state是响应式的,state改变会触发视图更新。实现方式就是在父级模块的state上用Vue.set添加子级模块的state,这样子模块的state变成响应式数据。

installGetters实现过程

const installGetters = (store, modules) => {
  const getters = modules._rawModule.getters;
  if (getters) {
    Object.keys(getters).forEach(getterName => {
      Object.defineProperty(store.get
ters, getterName, {
        get: () => {
          return getters[getterName](store.state);
        }
      });
    });
  }
};

installGetters就是将modulesgetters绑定到store上的getters属性上。通过Object.definePropertymodulesgetters属性名遍历绑定到storegetters上,在组件中获取store.getters上的属性实际上获取的是modules上的属性,相当于通过Object.defineProperty做了数据劫持。

installMutations实现过程

const installMutations = (store, modules) => {
  const mutations = modules._rawModule.mutations;
  if (mutations) {
    Object.keys(mutations).forEach(mutationName => {
      let temp =
        store.mutations[mutationName] || (store.mutations[mutationName] = []);
      temp.push((name, payload) => {
        mutations[name](modules.state, payload);
      });
    });
  }
};

installMutations就是将modulesmutations绑定到store上的mutations对象上。storemutations是对象,键名是mutations的函数名,键值是数组容器,存放所有同名的mutation函数,mutation是不怕重名的,重名的话将逐个执行。mutation函数需要两个参数,一个是mutationsstate,另一个是用户传入的参数,最终store上的mutations参考如下结构:

{
  mutation_city: [
    (state, val) => (state.state_city = "上海"),
    (state, val) => (state.state_city = val)
  ],
  mutation_country: [
    (state, val) => (state.state_country = val)
  ]
};

在组件中调用mutation的话是用this.$store.commit( "mutationName" , value )触发,那mutationcommit是怎么关联的呢?

commit = (mutationName, payload) => {
  this.mutations[mutationName].forEach(fn => fn(mutationName, payload));
}

commitstore类上的方法,它第一个参数为要触发的mutation名称,第二个为传入的值。它获取mutation上名字为mutationName的方法集合,然后传入payload依次执行。

installActions实现过程
installActions实现过程和installMutations实现过程差不多,就是触发mutation的为commit,而出发action的为dispatch

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • Vuex 源码学习 注释 源码目录 Vuex 核心 API: 插件安装 引入了 src/index.js 暴露的对...
    琉璃_xin阅读 359评论 0 0
  • 这几天忙啊,有绝地求生要上分,英雄联盟新赛季需要上分,就懒着什么也没写,很惭愧。这个vuex,vue-router...
    公子世无双ss阅读 650评论 0 0
  • 前言 之前几篇解析 Vue 源码的文章都是完整的分析整个源码的执行过程,这篇文章我会将重点放在核心原理的解析,不会...
    心_c2a2阅读 1,487评论 1 8
  • Vuex源码阅读分析 Vuex是专为Vue开发的统一状态管理工具。当我们的项目不是很复杂时,一些交互可以通过全局事...
    steinslin阅读 645评论 0 6
  • Vuex是一个专为Vue服务,用于管理页面数据状态、提供统一数据操作的生态系统。它集中于MVC模式中的Mo...
    01fb9c1e5eac阅读 1,039评论 1 3