之前阅读过 redux
的源码,vuex
同样也是一个状态管理工具,之前开发 vue 应用的时候,用到的 vuex 也比较多,所以自然对 vuex 颇有兴趣,最主要的原因是 我爱学习
,(不好意思,脸掉地上了),,哈哈哈哈哈哈
(其实过年这几天也挺无聊的。。。
接下来我们来回归正题
Vuex 是如何引入到 Vue 中的
我们都知道,在 Vue 中使用 Vuex 必须通过 Vue.use(Vuex) 方法来使用,所以先来瞄一瞄 Vue 对 Vuex 做了什么不可告人的事,惊恐.gif
来到 vue 源码下的 core/global-api/use.js
Vue.use = function (plugin: Function | Object) {
// 检测该插件是否已经被安装
if (plugin.installed) {
return
}
// use 支持传入一个选项对象,如
// Vue.use(MyPlugin, { someOption: true })
const args = toArray(arguments, 1) //获取可选对象
args.unshift(this) // 将Vue 构造器传入,当插件的第一个参数
if (typeof plugin.install === 'function') {
// install执行插件安装
plugin.install.apply(plugin, args)
} else if (typeof plugin === 'function') {
plugin.apply(null, args)
}
plugin.installed = true // 防止再次安装
return this
}
所以,vue 对插件还是挺温柔的,只用了它的一个安装函数进行插件的安装。好,我们来看看 vuex 的 install
函数
在 store.js 的最下面,我们看到了它的身影
function install(_Vue) {
if (Vue && _Vue === Vue) {
// 给出警告:说明重复安装
return
}
Vue = _Vue
applyMixin(Vue)
}
这里我们也可以看到 vuex 自己内部也做了一个限制,防止重复安装,最后该函数调用了 applyMixin(Vue)
咻一下,我们来到 mixin.js
,该函数也很简单,先是获取 Vue
的版本号,如果版本是 2.0 以上版本,就使用 Vue.mixin
来在所有组件中混入 beforeCreate
生命钩子,对应的处理函数是 vuexInit
,反之就向后兼容 1.x
版本
function vuexInit() {
// this -> vm
const options = this.$options
// store injection
if (options.store) { // 获取传入new Vue({store}) 里面的 store,并注册为 vm 的 $store 属性,这样我们就能在实例中使用 this.$store 访问 store 对象了
this.$store = typeof options.store === 'function' ?
options.store() :
options.store
} else if (options.parent && options.parent.$store) {
// 子组件从其父组件引用 $store 属性
this.$store = options.parent.$store
}
}
至此,前菜已经上齐了
主角 Store 闪亮登场
store.js
,别看长长一窜,有500多行,其实看进去了,你会感觉,也没啥可怕嘛
该文件主要由一个 Store
类和一些辅助函数构成,我们先来看 这个构造类
该构造函数中首先是进行一些必要的判断,如浏览器环境下自动安装、是否已经安装了 Vue、是否支持 Promise
,是否已经实例化了 Store 构造函数,其中用到了 assert
断言函数
// 构造函数
constructor(options = {}) {
// .......
}
// util.js
function assert (condition, msg) {
if (!condition) throw new Error(`[vuex] ${msg}`)
}
然后是从传入的options 中提取出 plugins
和 strict
,并做一些变量的初始化
// store internal state
// 表示提交状态,作用是保证对 Vuex 中 state 的修改只能在 mutation 的回调函数中,而不能在外部随意修改state
this._committing = false
// 用户定义的 actions
this._actions = Object.create(null)
// action 订阅者
this._actionSubscribers = []
// // 用户定义的 mutations
this._mutations = Object.create(null)
// 用户定义的 getters
this._wrappedGetters = Object.create(null)
// 收集用户定义的 modules
this._modules = new ModuleCollection(options)
// 模块命名空间map
this._modulesNamespaceMap = Object.create(null)
// 存储所有对 mutation 变化的订阅者,当执行commit时会执行队列中的函数
this._subscribers = []
// 创建一个 Vue 实例, 利用 $watch 监测 store 数据的变化
this._watcherVM = new Vue()
接着后面是对 dispatch 和 commit 函数中的 this 的重新绑定
const store = this
const {
dispatch,
commit
} = this
this.dispatch = function boundDispatch(type, payload) {
return dispatch.call(store, type, payload)
}
this.commit = function boundCommit(type, payload, options) {
return commit.call(store, type, payload, options)
}
这样做是因为在组件中通过 this.$store 直接调用 dispatch/commit 方法时, 能够使 dispatch/commit 方法中的 this 指向当前的 store 对象而不是当前组件的 this。
我们知道 new Vue
时会把传入的对象的中的 this
绑定为 vm
,例如 computed
属性,里面我们写如下代码时,会把计算属性里面的 this 绑定为当前的 vm 实例
computed: {
// 计算属性的 getter
reversedMessage: function () {
// `this` 指向 vm 实例
return this.message.split('').reverse().join('')
}
}
( 上面这段如有不妥,欢迎指出,谢谢 ^_^
接着就是 安装模块,vm 组件设置,传入插件以及 devtoolPlugin
插件的设置了
this.strict = strict
const state = this._modules.root.state
// init root module.
// this also recursively registers all sub-modules
// and collects all module getters inside this._wrappedGetters
installModule(this, state, [], this._modules.root)
// initialize the store vm, which is responsible for the reactivity
// (also registers _wrappedGetters as computed properties)
resetStoreVM(this, state)
// apply plugins
plugins.forEach(plugin => plugin(this))
if (Vue.config.devtools) {
devtoolPlugin(this)
}
接下来我们先不讲 dispatch
和 commit
是怎么实现的,先来重点关注下 modules
这部分,毕竟前面已经 new ModuleCollection(options)
,先来后到嘛,,哈哈
由于现在的篇幅也算可以了,怕大家看到长篇大论头疼,所以我们转移阵地。
(如有不当,欢迎指出,谢谢 ^_^
原文地址: 我看Vuex(一)