vuex 源码分析_Vuex2.0源码解析

本文通过一些简单流程图和文字说明介绍,用一种背离源码、更简单的方式去了解Vuex的原理。让我们在使用Vuex的时候明白背后的运行机制,方便我们更好的使用和调试解决问题。

1、什么是Vuex?

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

2、Vuex核心概念

在理解Vuex源码之前,有一些Vuex的核心概念需要简单介绍一下,进而帮助更好的理解源码。State:即状态,也就是Vuex核心管理的对象;

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

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

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

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

3、Vuex2.0源码结构

本文介绍Vuex2.0源码部分,首先看下整个源码构成,如下图:

Vuex源码部分总共包括五个部分:install:安装部分源码;

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

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

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

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

4、核心源码解析

4.1、install

安装部分源码的核心目的:给Vue注入一个store属性。

总体流程如下:

核心注入源码部分:

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的注册工作,先总体看下源码构成:

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

热更新:在注销一个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注册逻辑几近相似,逻辑如下:

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

getters的注册逻辑如下:

getters是为了获取派生状态,因此命名的定义不允许重复,首先根据模块类型进行重名判定,判定的依据来自内部属性:_wrappedGetters,这个属性存储着整个应用的注册getter。接下来就是将模块的getters按照类型存入即完成注册。最后是一个子模块的递归调用的方法。

installModule只是完成了模块的注册工作,离我们可以使用这些状态还有一些需要处理的代码。

4.2.2、resetStoreVM

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

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

mutations和actions使用通过store内部提供的两个重要api来实现,接下来介绍api部分。

5、api

5.1 commit 和 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,那么认为这次的修改是不正确的。 源码中还有一些内部api类似registerModule、unregisterModule、hotUpdate、watch以及subscribe等,在这里就不详细赘述。

6、辅助函数

Vuex除了上述提供的api以外,还提供了一些辅助函数,即操作 store 各种属性的一系列语法糖,目的是为了帮助我们使用Vuex的时候更方便。具体分为四个辅助函数: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 的内部API,例如travel-to-state、mutation钩子函数等,这样我们才可以根据自身的需求去开发相应的插件。

8、总结

本文介绍了Vuex2.0的源码核心,整体的源码量不大,通过一些简易流程和介绍说明源码运行机制,让大家基本在脱离源码的基础上简略理解Vuex的原理。

本文介绍了源码里比较核心的部分,源码里还有一些辅助函数和内部api值得推敲和品味,还是希望大家有机会去阅读一下完整的源码,从而更完整的理解Vuex,也更容易帮助你进行debug;其次通过通读源码,整体地理解它的设计理念以及编码风格,帮助你日后在迈向技术高工路上进行实践学习。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值