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 的根组件。主要包含了 state
和 mutation
。Store
创建了一个 data
为 state
的 Vue 实例,使用了 ES6 Class 的 get
和 set
对 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
注入了 vuexInit
,vuexInit
会在每个 instance 的 init hook 中调用。
第一步是绑定 store
, Vuex 会寻找 options 中的 store
作为实例的 $store
,在不存在时则以递归的方式寻找父组件中的 $store
,因此在 Vuex 的项目中,store
只需要在根组件中注入即可。
第二步是处理 vuex
, 分别处理其中的 getters
和 actions
, 以 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: {} |
| |
| } |
+--------------------+