Vuex安装
目录结构
import Vue from 'vue'
import Vuex from 'vuex'
//注册vuex
Vue.use(Vuex)
//安装时执行 install方法
export function install (_Vue) {
if (Vue && _Vue === Vue) {
if (process.env.NODE_ENV !== 'production') {
console.error(
'[vuex] already installed. Vue.use(Vuex) should be called only once.'
)
}
return
}
Vue = _Vue
applyMixin(Vue)
}
export default function (Vue) {
const version = Number(Vue.version.split('.')[0])
//版本判断
if (version >= 2) {
// vuexInit
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 每个组件都有store的实例
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
}
}
}
vuex注册时执行install方法,在 install 方法中, 调用了applyMixin 方法,applyMixin方法主要确保每个组件都有store的实例,都可以使用store实例
Store实例化
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
//不通过npm开发代码
if (!Vue && typeof window !== 'undefined' && window.Vue) {
//手动执行
install(window.Vue)
}
//非生产环境
if (process.env.NODE_ENV !== 'production') {
//注册完才能实例化
assert(Vue, `must call Vue.use(Vuex) before creating a store instance.`)
//当前环境是否支持promise
assert(typeof Promise !== 'undefined', `vuex requires a Promise polyfill in this browser.`)
//判断this是vuex的一个实例
assert(this instanceof Store, `store must be called with the new operator.`)
}
const {
//vue支持的插件
plugins = [],
strict = false
} = options
// store internal state
//实例store上的私有属性
this._committing = false
this._actions = Object.create(null)
this._actionSubscribers = []
this._mutations = Object.create(null)
this._wrappedGetters = Object.create(null)
//初始化modules的过程
this._modules = new ModuleCollection(options)
this._modulesNamespaceMap = Object.create(null)
this._subscribers = []
this._watcherVM = new Vue()
// bind commit and dispatch to self
const store = this
const { dispatch, commit } = this
this.dispatch = function boundDispatch (type, payload) {
//上下文为store
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
//actions,mutation 。。做赋值
installModule(this, state, [], this._modules.root)
// initialize the store vm, which is responsible for the reactivity
// (also registers _wrappedGetters as computed properties)
//getter,state建立依赖关系 响应式
resetStoreVM(this, state)
// apply plugins
//遍历plugins
plugins.forEach(plugin => plugin(this))
const useDevtools = options.devtools !== undefined ? options.devtools : Vue.config.devtools
if (useDevtools) {
devtoolPlugin(this)
}
}
...
}
store实例化过程主要定义私有属性的初始化,确保 dispatch/commit 方法中的 this 对象正确指向 store
Modules初始化
install把仓库拆分成小仓库,注册mutation,action,getter递归建立树形数据结构, installModule 接收5个参数: store、rootState、path、module、hot. store 表示当前 Store 实例, rootState 表示根 state, path 表示当前嵌套模块的路径数组, module 表示当前安装的模块
function installModule (store, rootState, path, module, hot) {
const isRoot = !path.length
const namespace = store._modules.getNamespace(path)
// register in namespace map
if (module.namespaced) {
if (store._modulesNamespaceMap[namespace] && process.env.NODE_ENV !== 'production') {
console.error(`[vuex] duplicate namespace ${namespace} for the namespaced module ${path.join('/')}`)
}
store._modulesNamespaceMap[namespace] = module
}
// set state
if (!isRoot && !hot) {
const parentState = getNestedState(rootState, path.slice(0, -1))
const moduleName = path[path.length - 1]
store._withCommit(() => {
Vue.set(parentState, moduleName, module.state)
})
}
//makeLocalContext 设置上下文
const local = module.context = makeLocalContext(store, namespace, path)
//做mutation 注册
module.forEachMutation((mutation, key) => {
const namespacedType = namespace + key
//创建mutation数组
registerMutation(store, namespacedType, mutation, local)
})
//做action注册
module.forEachAction((action, key) => {
const type = action.root ? key : namespace + key
const handler = action.handler || action
registerAction(store, type, handler, local)
})
//getter注册
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)
})
}
语法糖
//支持数组或对象 调用map方法 最后都会变成key value数组
function normalizeMap (map) {
return Array.isArray(map)
? map.map(key => ({ key, val: key }))
: Object.keys(map).map(key => ({ key, val: map[key] }))
}
//执行
function normalizeNamespace (fn) {
//两个参数 模块会传namespace 对两个参数,一个参数的做相应处理
return (namespace, map) => {
if (typeof namespace !== 'string') {
map = namespace
namespace = ''
//不传第一个参数,自动拼接斜线
} else if (namespace.charAt(namespace.length - 1) !== '/') {
namespace += '/'
}
return fn(namespace, map)
}
}
export const mapActions = normalizeNamespace((namespace, actions) => {
const res = {}
//遍历key value
normalizeMap(actions).forEach(({ key, val }) => {
//每一个值都是函数
res[key] = function mappedAction (...args) {
// get dispatch function from store
let dispatch = this.$store.dispatch
if (namespace) {
//返回相应模块
const module = getModuleByNamespace(this.$store, 'mapActions', namespace)
if (!module) {
return
}
dispatch = module.context.dispatch
}
return typeof val === 'function'
? val.apply(this, [dispatch].concat(args))
: dispatch.apply(this.$store, [val].concat(args))
}
})
return res
})
该方法会将 store 中的 dispatch 方法映射到组件的 methods 中
动态注册modules
//动态注入新的modules
registerModule (path, rawModule, options = {}) {
if (typeof path === 'string') path = [path]
//path不能为0 只能做扩展
if (process.env.NODE_ENV !== 'production') {
assert(Array.isArray(path), `module path must be a string or an Array.`)
assert(path.length > 0, 'cannot register the root module by using registerModule.')
}
//重新对modules做扩展
this._modules.register(path, rawModule)
//把新的模块的mutation,action 扩展进去
installModule(this, this.state, path, this._modules.get(path), options.preserveState)
// reset store to update getters...
resetStoreVM(this, this.state)
}
//注销
unregisterModule (path) {
if (typeof path === 'string') path = [path]
if (process.env.NODE_ENV !== 'production') {
assert(Array.isArray(path), `module path must be a string or an Array.`)
}
this._modules.unregister(path)
this._withCommit(() => {
const parentState = getNestedState(this.state, path.slice(0, -1))
Vue.delete(parentState, path[path.length - 1])
})
//做新modules 注销 ,重新整理action ,mutation ,,,
resetStore(this)
}