Vuex2.0源码浅析

1、什么是Vuex?

Vuex是一个专门为Vue.js应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。通俗一点理解即:针对组件繁多交互复杂的单页面应用,Vuex提供了一种便利、准确和可预测的状态管理方式,方便组件之间的数据共享和修改。

2、Vuex核心概念

  • State:即状态,也就是Vuex核心管理的对象;

  • Getters:派生状态,对state的二次包装(eg:默认后端时间戳转化为日期格式),Getters里的方法所有组件都可以使用;

  • Mutations:所有修改状态都是通过提交mutation,mutation类似事件,定义事件类型和回调函数,而回调函数就是进行状态修改的地方,状态修改一定是同步进行,从而确保状态修改可以被追踪到;

  • Actions:跟Mutations唯一不同的是进行异步修改状态,本质是在回调提交Mutation;

  • Modules:为了解决状态树庞大store臃肿问题,提出module概念,分化store到每个module,每个module都是一个小store。

3、Vuex2.0源码结构

本片文章介绍Vuex2.0源码部分,通过简单介绍了解背后的运行机制,让我们在使用更清楚其中的原理。首先看下整个源码构成,如下图:

clipboard.png

Vuex源码部分总共包括五个部分:

  • install:安装部分源码;

  • store:源码核心部分,本文重点介绍内容;

  • api:源码提供一些内部和外部api;

  • 辅助函数:语法糖,让我们在使用Vuex的时候书写更为简便;

  • plugin:提供一些默认插件,我们也可以自定义扩展插件的书写。

4、源码浅析

4.1、install

安装部分源码核心目的:给Vue注入一个Store属性。
总体流程如下:

clipboard.png

核心源码部分:

function vuexInit () {
    const options = this.$options
    // store 注入
    if (options.store) {
      this.$store = options.store
    } else if (options.parent && options.parent.$store) {
      this.$store = options.parent.$store
    }
}

4.2、store

store是整个Vuex的核心内容,其他几个部分的源码都是为了支撑store而存在的,store中完成所有组件共享数据的注册和调用修改方法。首先通过一个流程图看下store构造函数的构成:
图片描述

store像是一个生态环境,整个应用里的每个组件都要在这个环境里注册,完成状态的可预测管理。注册之前需要进行环境检测,这像是进入这个生态环境注册的门票,或者通行证;达到注册标准之后,进行一些内部属性和方法的初始化工作,这像是为注册工作搭建一个“舞台”,接下来的注册工作都在这个“舞台”上完成。

4.2.1、installModule

安装模块部分源码主要完成模块的state、mutations、actions和getters的注册工作,先总体看下源码构成:

clipboard.png

模块安装初期针对一些内部api的注册工作,接下来是state的更新工作,更新逻辑如下:

clipboard.png

热更新:在注销一个module的时候,其逻辑是安装一个空module达到更新的状态,而这个更新即为热更新。热更新的状态修改单独处理。

在获取父节点的状态之后,进行一次基于父节点状态的commit提交修改即可完成模块的state更新到state tree。源码如下:

if (!isRoot && !hot) {
    const parentState = getNestedState(rootState, path.slice(0, -1))
    const moduleName = path[path.length - 1]
    store._withCommit(() => {
      Vue.set(parentState, moduleName, module.state)
    })
}

接下来是一次store本地化的工作,即完成dispatch、commit、getters和state的本地化工作。本地化的目的是为了更好的进行数据方法操作。
mutations注册逻辑和actions注册逻辑几近相似,逻辑如下:

clipboard.png

在store环境里有两个属性:_mutations和_actions分别用来存储模块定义的mutations和actions,依据当前模块的类型进行查找对应的内部属性对象,并且将模块对应的回调函数进行包装插入到对应属性对象里,到此即完成注册工作。二者差别在于包装回调函数处理上的不同,mutation直接将回调包装起来即可,action对于回调函数的结果进行Promise对象的处理,然后包装。
getters的注册逻辑如下:

clipboard.png

getters是为了获取派生状态,因此不允许重名,首先根据模块类型进行重名判定,判定的依据来自内部属性:_wrappedGetters,这个属性存储着整个应用的注册getter。接下来就是将模块的getters按照类型存入即完成注册。最后是一个子模块的递归调用的方法。
installModule只是完成了模块的注册工作,离我们可以使用这些状态还有一些需要处理的代码。

4.2.2、resetStoreVM

这个方法是对state和getters进行最后处理,以至于让用户可以调用这些状态。源码逻辑如下:

clipboard.png

核心内容是store._vm这样一个内部变量,本质上将注册后的state和getters作为新的数据源实例化一个Vue对象传递给store._vm,并且删除旧的store._vm。与此同时,定义store.getters.xxx=store._vm[xxx],从而完成使用getters的正确姿势。state的使用是由store内部提供了一个api(store._vm.data.$$state.xxx),在更新store._vm之后,就可以访问这个模块的state。
mutations和actions使用通过store内部提供的两个重要api来实现,接下来介绍一个重要api。

5、api

5.1commit 和 dispatch

这两个api分别是用来完成mutations和actions的使用工作。源码逻辑如下:

图片描述

commit的逻辑是:从上面注册过的内部属性对象里拿到对应的mutations,然后通过_withCommit提交包装回调函数即可,同时使用内部api subscribe进行状态修改追踪订阅。而dispatch则是拿到注册的actions,然后promise.all执行回调,回调里则是进行commit提交。

5.2_withCommit
_withCommit (fn) {
    const committing = this._committing
    this._committing = true
    fn()
    this._committing = committing
 }

这个内部api是每次提交状态修改的核心源码,其逻辑很简单,在每次执行状态修改的时候,保证内部属性_committing为true,而这个属性的默认初始值为false。这样在追踪状态变化的时候,如果_committing不为true,那么认为这次的修改是不正确的。

6、辅助函数

Vuex除了上述提供的api以外,还提供了一些辅助函数,目的是为了帮助我们使用Vuex的时候更方便,提供了操作 store 的各种属性的一系列语法糖。具体分为四个辅助函数:mapState、mapMutations、mapActions和mapGetters。为了更清晰的解释语法糖的包装形式,先看一下使用方法:

computed: mapState({
    // 箭头函数可使代码更简练
    count: state => state.count,

    // 传字符串参数 'count' 等同于 `state => state.count`
    countAlias: 'count',

    // 为了能够使用 `this` 获取局部状态,必须使用常规函数
    countPlusLocalState (state) {
      return state.count + this.localCount
    }
  })

对state进行举例说明,以上代码是使用方法,从代码量上来看确实精简了不少,这样一个包装过程中重要的是将这个语法糖翻译成计算机可识别的js代码,因此在mapState的源码封装过程中实际做了很重要的一件事:将state注入到vue实例的computed属性里,同时对于不同格式的语法糖进行函数形式的解构包装即可。

同理可以对于mapMutations、mapActions和mapGetters进行解释:mapMutations和mapActions分别将commit 和 dispatch注入vue实例的methods里,而mapActions是将getters注入到vue实例的computed里,剩下的都是一些将语法糖书写改为函数形式即可,对于这几种的语法糖封装方式在源码上都大同小异。

7、plugin

vuex2.0里提供了两个plugin:devtool和logger。分别是接入开发者工具和输出state变化的log插件;从源码角度去看插件逻辑没什么特别说明,只是在开发插件的过程中可能需要对于内部提供的一些api和属性有更多的了解和掌握,例如subscribe,这样我们才可以根据自身的需求取开发相应的插件。

8、总结

本文介绍了Vuex2.0的源码,整个源码量不大,通过一些简易流程和介绍说明源码运行机制,让大家基本在脱离源码的基础上简略理解Vuex的原理。对于一个库和源码的研究,研究前和研究后一定要反复使用这个库,阅读前使用库帮助我们了解这是个什么库,可以做什么;阅读后使用库可以让我们从更多细节上去推敲使用上的细节,以及为什么这么使用,后者有没有更好的使用方式。

虽然源码读起来有点晦涩难懂,但是当你通读完以后可以帮助你了解内部的机制,从而更好地使用Vuex,也更容易帮助你进行debug;其次通过通读源码,整体地理解它的设计理念以及编码风格,帮助你日后在迈向技术高工路上进行实践学习。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值