以前阅读过 redux
的源码,vuex
一样也是一个状态管理工具,以前开发 vue 应用的时候,用到的 vuex 也比较多,因此天然对 vuex 很有兴趣,最主要的缘由是 我爱学习
,(很差意思,脸掉地上了),,哈哈哈哈哈哈 javascript
(其实过年这几天也挺无聊的。。。vue
接下来咱们来回归正题 java
咱们都知道,在 Vue 中使用 Vuex 必须经过 Vue.use(Vuex) 方法来使用,因此先来瞄一瞄 Vue 对 Vuex 作了什么不可告人的事,惊恐.gifreact
来到 vue 源码下的 core/global-api/use.js
git
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 的最下面,咱们看到了它的身影github
function install(_Vue) {
if (Vue && _Vue === Vue) {
// 给出警告:说明重复安装
return
}
Vue = _Vue
applyMixin(Vue)
}
复制代码
这里咱们也能够看到 vuex 本身内部也作了一个限制,防止重复安装,最后该函数调用了 applyMixin(Vue)
vuex
咻一下,咱们来到 mixin.js
,该函数也很简单,先是获取 Vue
的版本号,若是版本是 2.0 以上版本,就使用 Vue.mixin
来在全部组件中混入 beforeCreate
生命钩子,对应的处理函数是 vuexInit
,反之就向后兼容 1.x
版本redux
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
}
}
复制代码
至此,前菜已经上齐了api
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(一)