目錄結構vue
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實例react
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 對象正確指向 storevuex
install把倉庫拆分紅小倉庫,註冊mutation,action,getter遞歸創建樹形數據結構, installModule 接收5個參數: store、rootState、path、module、hot. store 表示當前 Store 實例, rootState 表示根 state, path 表示當前嵌套模塊的路徑數組, module 表示當前安裝的模塊npm
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
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)
}
複製代碼