vuex原理
浅析vuex的构成
vuex 引入 State
、Getter
的概念对状态进行定义;使用 Mutation
和 Action
对状态进行变更;引入Module
对状态进行模块化分割;引入插件对状态进行快照、记录、以及追踪等;提供了mapState、mapGetters、 mapActions、 mapMutations
辅助函数方便开发者在vm中处理store。具体构成关系如下:
浅析vuex的使用
vuex的使用方式很简单,本文为了剖析源码方便,只进行简单介绍。我们只需要利用vue的use机制将实例化后的store对象注入vue实例即可。如下图:
Vue.use(Vuex); // 1. vue的插件机制,安装vuex
let store = new Vuex.Store({ // 2.实例化store,调用install方法state,getters,modules,mutations,actions,plugins
});
new Vue({ // 3.注入store, 挂载vue实例store,render: h=>h(app)
}).$mount('#app');
疑问:vuex的store是如何注入到组件中的?
上面的代码得益于vue的插件机制,会在调用vuex的 install方法时,装载vuex。 所以我们直接关注 install方法的实现,其核心代码如下:
Vue.mixin({ beforeCreate: vuexInit });
可见,store注入 vue的实例组件的方式,是通过vue的 mixin机制,借助vue组件的生命周期 钩子 beforeCreate
完成的。即每个vue组件实例化过程中,会在 beforeCreate
钩子前调用 vuexInit
方法。下面,我们将焦点聚焦在 vuexInit 函数。 下面为 vuexInit 的核心代码
this.$store = typeof options.store === 'function'? options.store(): options.store
该代码的核心问题是this的指向,得益于mixin机制,this将指向 vue组件实例。最终,我们可以再 vue组件实例上获得vuex 的store 对象的引用 $store。图示如下:
结论:vuex利用了vue的mixin机制,混合 beforeCreate 钩子 将store注入至vue组件实例上,并注册了 vuex store的引用属性 $store!
疑问:vuex的state 和 getter 是如何映射到各个组件实例中自动更新的呢?
问题剖析
该问题的核心问题是当store中的 state 和 getter 方式变更时,vuex如何保证各个组件实例中的数据自动更新,并update 组件。简言之,某一组件store更新时,如何通知其他组件进行数据更新,和UI更新。通过简单分析可知,问题的根本就是组件通信的问题
浅谈组件通信
从分析可知,要解答本篇疑问,我们需要从 vue 组件通信谈起。在使用vue的过程中,需要频繁的进行组件间通信。通信的主体之间的关系可以是 父子组件,也可以是 类似兄弟组件或者是无关组件等非父子组件。总的来说有如下几种方式
- 通过props向子组件传递数据:父 -> 子
- 通过事件向父组件发送消息:子 -> 父,使用$emit发送事件
- 父链 和 子索引:
this.$parent
与this.$children
- 依赖注入:provide 和 inject
- 子组件引用: ref与$refs
- 特性绑定:
v-bind="$attrs"
和v-on="$listeners"
- event bus
$dispatch 和 $broadcast
: 在vue1中使用- 利用全局变量、storage、cookie、query、hash等传递数据: 非vue特性,不做赘述。
- 全局事件广播
我们探讨的是state 和 getter,首先我们先来看一下在vue组件中如何方便的获取 vuex的state和getter
this.$store.state.xxx;
this.$store.state.moduleA.xxx;this.$store.getters.xxx;
this.$store.getters.moduleA.xxx;
正如我们所知的,vuex的Store 会划分出 state 和 getters 两个数据区。getter是从store的state中派生出的状态,代码如下:
// 初始化store时,划分出 state数据区 与 getters数据区
new Vuex.Store({state, getters});
源码分析
首先,我们先来看state。从源码中我们找到了state的get方法
get state () {return this._vm._data.?state}
从源码得知,在vue组件中 使用 this.$store.getters.xxx
获取 xxx
属性时,实际上是获取的store._vm.data.?state
对象上的同名属性。那么我们将关注点放在 _vm上。我们通过Store 的constructor 找到了处理state 和 getter的核心函数resetStoreVM(this, state)
。其核心代码如下:
store._vm = new Vue({data: {?state: state}})
上述代码初始化了一个vue实例 _vm,由于vue的data是响应式的,所以,?state也是响应式的,那么当我们 在一个组件实例中 对state.xxx进行 更新时,基于vue的data的响应式机制,所有相关组件的state.xxx的值都会自动更新,UI自然也会自动更新!可见,这和vue的中央事件总线 设计思想如出一辙,同样借助 vue对象特性(响应式的data)作为其他组件的通信桥梁,实现组件间的通信以及数据共享
结论:
- vuex的state是借助vue的响应式data实现的。
- getter是借助vue的计算属性computed特性实现的。
- 其设计思想与vue中央事件总线如出一辙。