Vuex 源码阅读

2016/12/26

Vuex 作为 Vue 官方的状态管理架构,借鉴了 Flux 的设计思想,在大型应用中可以理清应用状态管理的逻辑。为了更清楚的理解它的原理和实现,还是从源码开始读起吧。总共 1000 多行的代码,读起来也相对轻松。

cloc src/

------------------------------------------------------------------
Language   files          blank        comment         code
------------------------------------------------------------------
JavaScript   5             53            141            389
------------------------------------------------------------------
SUM:         5             53            141            389
------------------------------------------------------------------

cloc test/

------------------------------------------------------------------
Language   files          blank        comment         code
------------------------------------------------------------------
JavaScript   5             62             30            793
------------------------------------------------------------------
SUM:         5             62             30            793
------------------------------------------------------------------

结构梳理

先抛开 middlewares,Vuex 的主要源码一共有三个文件:

file intro
index.js Class Store, install,...
override.js 初始化 Vuex
util.js 相关 util(用到了 getWatcher 和 getDeep)

Store

我们使用 Store 创建 Vuex 的实例并传递给 Vue 的根组件。主要包含了 statemutationStore 创建了一个 datastate 的 Vue 实例,使用了 ES6 Class 的 getset 对 state 做了映射,对 state 的重新 set 当然是不允许的,get 则映射到了 this._vm._data

Store 提供了 dispatch 方法来完成对 state 的修改,和想象中的差不多,在 _mutations 里找到对应 type 的 mutation,参数并入 this.state 传参调用。

override

作为一个 Vue 的插件,Vuex 需要被这样引入:

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

Vue 的插件应当有一个公开方法 install。这个方法的第一个参数是 Vue 构造器。 Vuex 的 install 中,在保证单次调用的情况下,调用 override 对 Vue 构造器进入了注入。

override 中对 Vue.prototype._init 注入了 vuexInitvuexInit 会在每个 instance 的 init hook 中调用。

第一步是绑定 store, Vuex 会寻找 options 中的 store 作为实例的 $store,在不存在时则以递归的方式寻找父组件中的 $store,因此在 Vuex 的项目中,store 只需要在根组件中注入即可。

第二步是处理 vuex, 分别处理其中的 gettersactions, 以 example/counter/Counter.vue 为例:

getters

Vuex 用 Object.defineProperty 为每个 getter 在 vm 上绑定了 data,特别的是 getter 作为单向仅 get 数据流,并不能被 set,所以对应的 setter 为报错用的空函数。而 getter 的原理类似于 computed getter,特别的是使用了 store 的 uniqueId 为标识做了缓存,这样同一个 getter 在所有组件中都会使用相同的 watcher。

setter

Action 相对要简单一些,以 $store 作为 action 第一个参数,并将 action 绑定在 instance 上。形成了一个闭环,让 action 访问到 store。

总结

Vuex 源码上粗略的分析基本就到这里了,其实很多地方的代码都很值得细细研究,比如 Store 中的 middlewares 可以完成一些神奇的事情,这里就不一一分析了,画了一张图,按源码的思路大概表达下数据流的意思。O(∩_∩)O

      +-----------+
      |           |
      |   Store   +>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>+
      |           |                                   v
      |-----------|                                   v
      |   state   <<<<<<+                             v
      |-----------|     ^                             v
+>>>>>- distapatch ->>>>+                             v
^     +-----------+                                   v
^                                                     v
^                          +--------------------+     v
^                          |                    |     v
^                          |     Component      |     v
^                          |                    |     v
^                          |--------------------|     v
^                    +<<<<<-----  $store  <------<<<<<+
^                    v     |--------------------|
^                    v     |  vuex: {           |
^                    v     |                    |
^                    +>>>>>----- getters: {},   |
^                          |                    |
+<<<<<<<<<<<<<<<<<<<<<<<<<<----- actions: {}    |
                           |                    |
                           |  }                 |
                           +--------------------+