源码阅读vuex
Vue.use
在vue项目中使用路由时,需要通过Vue.use(Vuex)
注册状态管理。
在vue源码文件vue/src/core/index.js中,调用了initGlobalAPI(Vue)
方法,
在vue/src/core/global-api/index.js中,定义并导出了initGlobalAPI
方法,
在initGlobalAPI
方法中调用了initUse(Vue)
,在vue/src/core/global-api/use.js中,找到initUse
,
在这里定义了Vue.use
。
export function initUse (Vue: GlobalAPI) {
Vue.use = function (plugin: Function | Object) {
// 获取已经注册的插件列表
const installedPlugins = (this._installedPlugins || (this._installedPlugins = []))
// 避免重复注册
if (installedPlugins.indexOf(plugin) > -1) {
return this
}
// 获取其他参数
const args = toArray(arguments, 1)
// 向参数数组首部推入当前vue实例
args.unshift(this)
// 当组件有install方法时调用install,或者当组件本身是一个可调用的方法时,调用组件方法,以此完成注册动作
if (typeof plugin.install === 'function') {
plugin.install.apply(plugin, args)
} else if (typeof plugin === 'function') {
plugin.apply(null, args)
}
// 向已注册数组增加注册组件
installedPlugins.push(plugin)
return this
}
}
在vue/src/shared/util.js中,找到toArray
方法,用于将一个类数组对象转换成数组。
export function toArray (list: any, start?: number): Array<any> {
start = start || 0
let i = list.length - start
const ret: Array<any> = new Array(i)
while (i--) {
ret[i] = list[i + start]
}
return ret
}
install + applyMixin
在vuex源码vue-router/src/store.js中导出了install方法。
import applyMixin from './mixin'
export function install (_Vue) {
// 避免重复注册
if (Vue && _Vue === Vue) {
if (__DEV__) {
console.error(
'[vuex] already installed. Vue.use(Vuex) should be called only once.'
)
}
return
}
Vue = _Vue
applyMixin(Vue)
}
install 做了重复注册校验,并调用了applyMixin方法,applyMixin来自vue-router/src/mixin.js。
export default function (Vue) {
const version = Number(Vue.version.split('.')[0])
if (version >= 2) {
Vue.mixin({
beforeCreate: vuexInit })
} else {
// override init and inject vuex init procedure
// for 1.x backwards compatibility.
const _init = Vue.prototype._init
Vue.prototype._init = function (options = {
}) {
options.init = options.init
? [vuexInit].concat(options.init)
: vuexInit
_init.call(this, options)
}
}
/**
* Vuex init hook, injected into each instances init hooks list.
*/
function vuexInit () {
const options = this.$options
// store injection
if (options.store) {
this.$store = typeof options.store === 'function'
? options.store()
: options.store
} else if (options.parent && options.parent.$store) {
this.$store = options.parent.$store
}
}
}
applyMixin 做了一个版本判断,如果vue版本大于等于2,则在vue的beforeCreate生命周期混入一个vuexInit方法,如果版本小于2,则重写了vue的_init方法,将vuexInit加到_init参数options的init属性上,然后执行原来的_init内容。
vuexInit的内容也很简单,判断当前vue实例上有没有store,有的话就将 s t o r e 指 向 t h i s . store指向this. store指向this.options.store,如果这个store是一个方法,就执行拿到它的结果再赋值,如果当前vue实例没有store,就从父组件拿。这就保证了全局只有一个sotre,达到所有组件共用状态的目的。
Store
来看一下通常我们是怎么使用vuex的:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
}
})
new Vue({
el: '#app',
store
})
可以看到Vue实例化传入的store对象,是通过new Vuex.Store({})
实例化生成的,那么接下来,就看一下Store构造函数。
源码跟install方法在同一个文件中:vue-router/src/store.js
export class Store {
constructor (options = {
}) {
// 如果还未注册,在有window.Vue的情况下,会自动注册
if (!Vue && typeof window !== 'undefined' && window.Vue) {
install(window.Vue)
}
// 开发环境下的一些报错
if (__DEV__) {
assert(Vue, `must call Vue.use(Vuex) before creating a store instance.`)
assert(typeof Promise !== 'undefined', `vuex requires a Promise polyfill in this browser.`)
assert(this instanceof Store, `store must be called with the new operator.`)
}
const {
// 插件数组,vuex 插件暴露出每次 mutation 的钩子
plugins = [],
// 严格模式设置,在严格模式下,无论何时发生了状态变更且不是由 mutation 函数引起的,将会抛出错误。
strict = false
} = options
// 各种内部属性初始化
this._committing = false // 用来判断严格模式下是否是用mutation修改state
this._actions = Object.create(null)
this._actionSubscribers = []
this._mutations = Object.create(null)
this._wrappedGetters = Object.create(null)
this._modu