上一篇文章说的是如何阅读框架源代码,收到了“若是更详细一点就行了”的反馈,不如就以 Vuex 为切入点进行一次实践吧,不矫揉不造做,说走咱就走~~html
本文假定你已经对 Vue 的使用上有必定的概念,不要求轻车熟路(使用过 Vuex 固然是最好的),但至少要了解基本的事件绑定方式,以及 Mixin 的用法,官方文档今后去前端
官方的说法:Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式
这里首先要搞清楚什么是状态,状态就是数据,也就是说: Vuex 提供了一套 Vue 应用统一的数据源管理模式,除了定义数据源,还定义了数据的管理模式vue
这其中,Store 所包含的两个核心部分 State 和 Actions 分别表明了数据源,和数据的管理(操做)模式,同时做为一个全局的 VM,其有效的协调了 Vue 各组件间的通讯vuex
若是读 Vue 文档的时候足够留心,兴许你能在插件一节找到蛛丝马迹:浏览器
插件的功能包括,经过全局 mixin 方法添加一些组件选项,如:vuexapp
也就是说,Vuex 不过是 Vue 的一个插件,经过 Mixin 的方式给每一个组件注入一个 $store 对象,因为每一个组件的 $store 指向的是同一个 store 对象(后面经过详读代码能够知道,这个 $store 实际上是一个 VM 对象),因此 store 是全局的,这就印证了以前在咱们为何须要 Vuex中的一个结论,Vuex 相似于一个事件总线框架
从入口文件 index.js 开始,代码很少,能够直接贴出来ide
export default {
Store,
install,
version: '__VERSION__',
mapState,
mapMutations,
mapGetters,
mapActions
}复制代码
若是你一眼就看出这里的关键是 install,那么你应该领略到读源码先了解设计思想的独特魅力了,没错,做为 Vue 的 Plugin,install 方法就是入口函数
循着 install 方法进入 store.js,仍是符合预期,这个方法主要干得是事情就是 mixinpost
export function install (_Vue) {
...
Vue = _Vue
applyMixin(Vue)
}
// auto install in dist mode
if (typeof window !== 'undefined' && window.Vue) {
install(window.Vue)
}复制代码
而且还有一个小细节,浏览器环境下而且 Vue 不为空的时候,引入 Vuex 以后是会自动注册的
具体来看看 mixin.js 这个文件,划重点(注意看注释):
// 经过钩子 init / beforeCreate 执行 vuexInit
const usesInit = Vue.config._lifecycleHooks.indexOf('init') > -1
Vue.mixin(usesInit ? { init: vuexInit } : { beforeCreate: vuexInit })
// 组件初始化的时候注入 $store
function vuexInit () {
const options = this.$options
// store injection
if (options.store) {
this.$store = options.store
} else if (options.parent && options.parent.$store) {
this.$store = options.parent.$store
}
}复制代码
Vuex 的最佳实践中,通常这样使用(带着目标去阅读,效果更佳):
// create store
const store = new Vuex.Store({
actions: {
...
},
modules: {
...
}
})
import App from './comps/app.vue'
new Vue(Vue.util.extend({ el: '#root', store }, App))复制代码
咱们须要新建一个 Store,在建立 Vue 实例的时候,做为参数传入,在上一节的 vuexInit 函数中,是从 this.$options 中取出 store 赋值给组件的 $store 的,如此,便能无缝联系上了
接下来的重点,就是 Store 这个类了,仍是 store.js 这个文件,怀着入参为 ations 和 modules 的预期,来读 constructor 方法,却是有一个语句是用来处理 modules 的
this._modules = new ModuleCollection(options)复制代码
但真的是寻寻觅觅寻不到从 options 中取出 actions 进行处理的方法,固然后面仔细阅读了 ModuleCollection 中的代码以后,才找到了答案,actions 参数也是在这里面提取的。毕竟让我纠结迷茫了良久,若是是我来写的话,我可能不会这么写,方法的命名须要有语义性,并且一个方法也应当只作一件事情
原则上为了尽快理清主流程,有些细节须要暂时略过(因此语义化的命名、合理的函数拆分,对阅读者来讲是多么的重要),假设已经知道前面的步骤已经从 options 中读到了 actions 和 modules,那么下一个核心节点就是:
installModule(this, state, [], this._modules.root)复制代码
这一步再进行分解(注意看注释)
// 注册 mutation
module.forEachMutation((mutation, key) => {
const namespacedType = namespace + key
registerMutation(store, namespacedType, mutation, local)
})
// 注册 action
module.forEachAction((action, key) => {
const namespacedType = namespace + key
registerAction(store, namespacedType, action, local)
})
// 注册 getter (computed)
module.forEachGetter((getter, key) => {
const namespacedType = namespace + key
registerGetter(store, namespacedType, getter, local)
})
// 遍历子模块
module.forEachChild((child, key) => {
installModule(store, rootState, path.concat(key), child, hot)
})复制代码
出于篇幅以及但愿阅读的同窗亲自实践的目的,具体的注册方式这里再也不展开
进入下一个重要环节 resetStoreVM,建立 VM,实现数据监听(注意看注释)
function resetStoreVM (store, state, hot) {
// bind store public getters
// getters 其实就是 computed
store.getters = {}
const wrappedGetters = store._wrappedGetters
const computed = {}
forEachValue(wrappedGetters, (fn, key) => {
// use computed to leverage its lazy-caching mechanism
computed[key] = () => fn(store)
Object.defineProperty(store.getters, key, {
get: () => store._vm[key],
enumerable: true // for local getters
})
})
// 建立一个 Vue 实例,做为 Store 的 VM
store._vm = new Vue({
data: {
$$state: state
},
computed
})
...
}复制代码
至此,Vuex 的主流程代码基本上算是走了一遍,看似神奇,但是代码量并不大,仍是那句话,但愿阅读的同窗可以按照这个套路本身走一遍
本文在公众号菲麦前端同步发行: