一、vuex初始化
当使用import Vuex from ‘vuex的时候,会找到package.json中的module也就是dist/vuex.esm.js,vuex和vue-router和vue相同,都是通过rollup进行构建,当执行npm run build的时候,会执行npm run build:main,也就是node scripts/build-main.js,通过rollup.main.config.js进行构建,rollup.main.config.js中input为src/index.js打包出的file是dist/vuex.esm.js,也就是说,vuex的主入口是src/index.js。src/index.js文件中暴露了各种方法,其中在使用过程中,会先通过Vue.use(Vuex)进行插件的注册,在分析vue-router的时候,我们可以知道Vue.use实际会执行插件export出的install方法。vuex的install方法会执行会判断全局定义的Vue是否存在,并且是否等于install传入的参数Vue,如果相同,那么会结束函数执行,这是为了防止多次的执行use,保证只会执行一次,这和vue-router如出一辙。如果是首次use,那么会给全局的Vue赋值为传入的Vue,接着执行applyMixin(Vue)。applyMixin函数首先会拿到当前Vue的主版本号,如果版本号小于2,那么会通过重写Vue.prototype._init的方式把vuex作为init函数的参数传入,以此挂载vuex,如果大于等于2版本,那么会通过Vue.mixin,把vuexInit函数作为beforeCreate生命周期的执行函数。vuexInit函数会先通过this.$options.store拿到在new Vue()的时候传入的store,如果拿到store,那么说明这是一个根部实例,会去判断store是否是一个函数,如果是函数,那么会去执行并赋值给this.$store,如果不是函数,则直接赋值。如果没有拿到this.$options.store,则说明当前并不是根部实例,他会去尝试拿到this.$options.parent.$store,也就是父组件的$store,挂载到当前的this.$store上,这也就保证了在全局的任何地方,我们都可以通过this.$store访问到store实例。为什么不通过Vue.prototype.$store的方式挂载,因为这样挂载,只是对当前的组件实例上,添加了$store,而不是原型上,这样对Vue构造函数的侵入性小。
// 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
}
}
}
当执行new Vuex.store()的时候,会执行Store的constructor。首先会判断是否有use传入的Vue并且有window以及window.Vue,如果满足,则说明vuex的使用并不是通过npm的方式引入,而是通过外链的方式,那么他会执行install(window.Vue),去主动的调用一次install进行vuex的挂载。之后他会判断是否有了全局的Vue,如果没有则说明,在new store实例前,没有通过use进行正确的vuex注册(全局vue在install方法中进行了赋值),然后判断是否有Promise,这说明vuex是依赖于promise的。最后判断this也就是当前的实例,是否是被Store构造出来,如果不是,则说明没有使用new运算符构造vuex实例,而是直接调用了store。之后定义了_committing,_actions,_modules等私有变量。之后从this当中拿出了dispatch和commit,通过call改变他们的this指向,这样之后如果使用者试图通过call改变他们的this指向,他们的this依然会指向创建时候的store实例。然后创建了state,调用了installModule初始化根部模块,注册所有的sub-modules,并且收集了所有模块的getter放到this._wrappedGetters,接着调用了resetStoreVM,使store变成一个响应式的,也去注册了_wrappedGetters使他成为一个computed。
export class Store {
constructor (options = {}) {
// Auto install if it is not done yet and `window` has `Vue`.
// To allow users to avoid auto-installation in some cases,
// this code should be placed here. See #731
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 {
plugins = [],
strict = false
} = options
// store internal state
this._committing = false
this._actions = Object.create(null)
this._actionSubscribers = []
this._mutations = Object.create(null)
this._wrappedGetters = Object.create(null)
this._modules = new ModuleCollection(options)
this._modulesNamespaceMap = Object.create(null)
this._subscribers = []
this._watcherVM = new Vue()
this._makeLocalGettersCache = Object.create(null)
// bind commit and dispatch to self
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)
}
// strict mode
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)
...
}
}