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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343

推荐阅读更多精彩内容

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