标签:local 匹配 aio product catch sse $watch 不同 string
最终也是最重要的store.js
,该文件主要涉及的内容如下:
let Vue
上面这些和这个Vue
都在同一个作用域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)
}
解析:
beforeCreate
期间给所有组件创建$store
Vue.use(Vuex)
unifyObjectStyle
function unifyObjectStyle (type, payload, options) {
if (isObject(type) && type.type) {
options = payload
payload = type
type = type.type
}
if (process.env.NODE_ENV !== ‘production‘) {
assert(typeof type === ‘string‘, `expects string as the type, but found ${typeof type}.`)
}
return { type, payload, options }
}
解析:
dispatch(‘pushTab‘, payload, options)
和 dispatch({ type: ‘pushTab‘, payload }, options)
这种情况getNestedState
function getNestedState (state, path) {
return path.length
? path.reduce((state, key) => state[key], state)
: state
}
解析:
[]
为非嵌套或根模块,{ shop: { card: { item: 1 } } }
的path为[‘shop‘, ‘card‘, ‘item‘]
item
[{ name: ‘getMe‘ }, { name: ‘notMe‘ }]
,要获取‘getMe‘
那么const path = [0, ‘name‘]
enableStrictMode
function enableStrictMode (store) {
store._vm.$watch(function () { return this._data.$$state }, () => {
if (process.env.NODE_ENV !== ‘production‘) {
assert(store._committing, `do not mutate vuex store state outside mutation handlers.`)
}
}, { deep: true, sync: true })
}
解析:
$watch
就是Vue.$watch
,主要功能是:如果启动了严格模式,监控数据状态的改变是不是通过commit
改变的。commit
改变状态的,在开发模式下会提示。VueX
的状态是存在于一个Vue实例中的_data.$$state
,Store._vm
可以解释VueX
为什么叫VueX
VueX
是Vue的一个实例
genericSubscribe
function genericSubscribe (fn, subs) {
if (subs.indexOf(fn) < 0) {
subs.push(fn)
}
return () => {
const i = subs.indexOf(fn)
if (i > -1) {
subs.splice(i, 1)
}
}
}
解析:
subs
是观察者,subs
关联到某个状态,那个状态改变,会遍历subs
调用里面的函数。const unsubscribe = genericSubscribe(fn, subs)
,调用unsubscribe()
就可以取消订阅几个辅助函数和状态相关
context
let Vue // bind on install
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 (process.env.NODE_ENV !== ‘production‘) {
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()
// 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)
// apply plugins
plugins.forEach(plugin => plugin(this))
if (Vue.config.devtools) {
devtoolPlugin(this)
}
}
get state () {
return this._vm._data.$$state
}
set state (v) {
if (process.env.NODE_ENV !== ‘production‘) {
assert(false, `use store.replaceState() to explicit replace store state.`)
}
}
commit (_type, _payload, _options) {
// check object-style commit
const {
type,
payload,
options
} = unifyObjectStyle(_type, _payload, _options)
const mutation = { type, payload }
const entry = this._mutations[type]
if (!entry) {
if (process.env.NODE_ENV !== ‘production‘) {
console.error(`[vuex] unknown mutation type: ${type}`)
}
return
}
this._withCommit(() => {
entry.forEach(function commitIterator (handler) {
handler(payload)
})
})
this._subscribers.forEach(sub => sub(mutation, this.state))
if (
process.env.NODE_ENV !== ‘production‘ &&
options && options.silent
) {
console.warn(
`[vuex] mutation type: ${type}. Silent option has been removed. ` +
‘Use the filter functionality in the vue-devtools‘
)
}
}
dispatch (_type, _payload) {
// check object-style dispatch
const {
type,
payload
} = unifyObjectStyle(_type, _payload)
const action = { type, payload }
const entry = this._actions[type]
if (!entry) {
if (process.env.NODE_ENV !== ‘production‘) {
console.error(`[vuex] unknown action type: ${type}`)
}
return
}
this._actionSubscribers.forEach(sub => sub(action, this.state))
return entry.length > 1
? Promise.all(entry.map(handler => handler(payload)))
: entry[0](payload)
}
subscribe (fn) {
return genericSubscribe(fn, this._subscribers)
}
subscribeAction (fn) {
return genericSubscribe(fn, this._actionSubscribers)
}
watch (getter, cb, options) {
if (process.env.NODE_ENV !== ‘production‘) {
assert(typeof getter === ‘function‘, `store.watch only accepts a function.`)
}
return this._watcherVM.$watch(() => getter(this.state, this.getters), cb, options)
}
replaceState (state) {
this._withCommit(() => {
this._vm._data.$$state = state
})
}
registerModule (path, rawModule, options = {}) {
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.`)
assert(path.length > 0, ‘cannot register the root module by using registerModule.‘)
}
this._modules.register(path, rawModule)
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])
})
resetStore(this)
}
hotUpdate (newOptions) {
this._modules.update(newOptions)
resetStore(this, true)
}
_withCommit (fn) {
const committing = this._committing
this._committing = true
fn()
this._committing = committing
}
}
解析:
install(window.Vue)
如果是Vue2,通过mixins的方式添加beforeCreate
钩子函数,把store传给任何继承这个Vue的实例(组件),所以所有组件都拥有一个$store的属性。也即每个组件都能拿到store。
第二个断言判断是确保Vue在当前环境中,且需要Promise,强制Store作为构造函数。
可配置项有两个值plugins
和是否使用strict
严格模式(只能通过commit改变状态),plugins
一般用于开发调试
将全局(非模块内)的dispatch和commit
的this
绑定到store
Store类的静态属性
_committing
:记录当前是否commit中_actions
:记录所有action的字段,有模块作用域的用‘/‘分隔_actionSubscribers
:全局的dispatch后遍历调用数组中的函数_mutations
:和actions一样_wrappedGetters
:不管是使用了模块还是全局的getter都存在于此_modules
:全局模块_modulesNamespaceMap
:所有模块全存于此,可通过namespace取出想要的模块_subscribers
:全局的commit调用之后,遍历调用数组中的函数_watcherVM
:Vue实例用于watch()
函数strict
:是否使用严格模式,使用那么改变状态只能通过commit来改变状态state
:Store的所有state包含模块的_vm
:Vue的实例,VueX
真正状态是存于这里Store._vm._data.$$state
installModule
function installModule (store, rootState, path, module, hot) {
const isRoot = !path.length
const namespace = store._modules.getNamespace(path)
// register in namespace map
if (module.namespaced) {
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)
})
}
const local = module.context = makeLocalContext(store, namespace, path)
module.forEachMutation((mutation, key) => {
const namespacedType = namespace + key
registerMutation(store, namespacedType, mutation, local)
})
module.forEachAction((action, key) => {
const type = action.root ? key : namespace + key
const handler = action.handler || action
registerAction(store, type, handler, local)
})
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)
})
}
this._wrappedGetters
中。_modulesNamespaceMap
存放所有模块,可以通过namespaced来获取模块(数据结构:哈希表)Vue.set(parentState, moduleName, module.state)
由于VueX
是Vue的实例,Vue设置的状态,它的实例(VueX)可以继承context
makeLocalContext
/**
* make localized dispatch, commit, getters and state
* if there is no namespace, just use root ones
*/
function makeLocalContext (store, namespace, path) {
const noNamespace = namespace === ‘‘
const local = {
dispatch: noNamespace ? store.dispatch : (_type, _payload, _options) => {
const args = unifyObjectStyle(_type, _payload, _options)
const { payload, options } = args
let { type } = args
if (!options || !options.root) {
type = namespace + type
if (process.env.NODE_ENV !== ‘production‘ && !store._actions[type]) {
console.error(`[vuex] unknown local action type: ${args.type}, global type: ${type}`)
return
}
}
return store.dispatch(type, payload)
},
commit: noNamespace ? store.commit : (_type, _payload, _options) => {
const args = unifyObjectStyle(_type, _payload, _options)
const { payload, options } = args
let { type } = args
if (!options || !options.root) {
type = namespace + type
if (process.env.NODE_ENV !== ‘production‘ && !store._mutations[type]) {
console.error(`[vuex] unknown local mutation type: ${args.type}, global type: ${type}`)
return
}
}
store.commit(type, payload, options)
}
}
// getters and state object must be gotten lazily
// because they will be changed by vm update
Object.defineProperties(local, {
getters: {
get: noNamespace
? () => store.getters
: () => makeLocalGetters(store, namespace)
},
state: {
get: () => getNestedState(store.state, path)
}
})
return local
}
context
() => store.getters
,只有调用这个函数才获取到所有getters,结合get
就是只有引用这个属性才会获取所有getters() => fn()
要留意的是:是fn()
而不是fn
,fn()
才是要执行的命令
makeLocalGetters
function makeLocalGetters (store, namespace) {
const gettersProxy = {}
const splitPos = namespace.length
Object.keys(store.getters).forEach(type => {
// skip if the target getter is not match this namespace
if (type.slice(0, splitPos) !== namespace) return
// extract local getter type
const localType = type.slice(splitPos)
// Add a port to the getters proxy.
// Define as getter property because
// we do not want to evaluate the getters in this time.
Object.defineProperty(gettersProxy, localType, {
get: () => store.getters[type],
enumerable: true
})
})
return gettersProxy
}
resetStoreVM
function resetStoreVM (store, state, hot) {
const oldVm = store._vm
// bind store public getters
store.getters = {}
const wrappedGetters = store._wrappedGetters
const computed = {}
forEachValue(wrappedGetters, (fn, key) => {
// use computed to leverage its lazy-caching mechanism
computed[key] = () => fn(store)
Object.defineProperty(store.getters, key, {
get: () => store._vm[key],
enumerable: true // for local getters
})
})
// use a Vue instance to store the state tree
// suppress warnings just in case the user has added
// some funky global mixins
const silent = Vue.config.silent
Vue.config.silent = true
store._vm = new Vue({
data: {
$$state: state
},
computed
})
Vue.config.silent = silent
// enable strict mode for new vm
if (store.strict) {
enableStrictMode(store)
}
if (oldVm) {
if (hot) {
// dispatch changes in all subscribed watchers
// to force getter re-evaluation for hot reloading.
store._withCommit(() => {
oldVm._data.$$state = null
})
}
Vue.nextTick(() => oldVm.$destroy())
}
}
store._vm
的值_vm
的$$state保存的是store.state_vm
的computed
是store._wrappedGetters
的值VueX
的状态是存在于一个Vue实例
的$data中的VueX
内部的_vm
的,也可以认为就是gettersregisterMutation
function registerMutation (store, type, handler, local) {
const entry = store._mutations[type] || (store._mutations[type] = [])
entry.push(function wrappedMutationHandler (payload) {
handler.call(store, local.state, payload)
})
}
store._mutations[type]
,type就是命名空间,按命名空间来存放mutationsregisterAction
function registerAction (store, type, handler, local) {
const entry = store._actions[type] || (store._actions[type] = [])
entry.push(function wrappedActionHandler (payload, cb) {
let res = handler.call(store, {
dispatch: local.dispatch,
commit: local.commit,
getters: local.getters,
state: local.state,
rootGetters: store.getters,
rootState: store.state
}, payload, cb)
if (!isPromise(res)) {
res = Promise.resolve(res)
}
if (store._devtoolHook) {
return res.catch(err => {
store._devtoolHook.emit(‘vuex:error‘, err)
throw err
})
} else {
return res
}
})
}
store._actions[type]
存储结构和_mutations
的一样,按命名空间来存储action{ dispatch, commit, getters, state, rootGetters, rootState }
{ dispatch, commit, getters, state }
是局部local的registerGetter
function registerGetter (store, type, rawGetter, local) {
if (store._wrappedGetters[type]) {
if (process.env.NODE_ENV !== ‘production‘) {
console.error(`[vuex] duplicate getter key: ${type}`)
}
return
}
store._wrappedGetters[type] = function wrappedGetter (store) {
return rawGetter(
local.state, // local state
local.getters, // local getters
store.state, // root state
store.getters // root getters
)
}
}
store._wrappedGetters[type]
由于所有getters放在一个对象,结构和actions、mutations的结构就不一样了,就是一个对象resetStore
function resetStore (store, hot) {
store._actions = Object.create(null)
store._mutations = Object.create(null)
store._wrappedGetters = Object.create(null)
store._modulesNamespaceMap = Object.create(null)
const state = store.state
// init all modules
installModule(store, state, [], store._modules.root, true)
// reset vm
resetStoreVM(store, state, hot)
}
installModule
和resetStoreVM
state()
get state () {
return this._vm._data.$$state
}
set state (v) {
if (process.env.NODE_ENV !== ‘production‘) {
assert(false, `use store.replaceState() to explicit replace store state.`)
}
}
store._vm._data.$$state
中获取replaceState
replaceState (state) {
this._withCommit(() => {
this._vm._data.$$state = state
})
}
store._vm._data.$$state
commit
commit (_type, _payload, _options) {
// check object-style commit
const {
type,
payload,
options
} = unifyObjectStyle(_type, _payload, _options)
const mutation = { type, payload }
const entry = this._mutations[type]
if (!entry) {
if (process.env.NODE_ENV !== ‘production‘) {
console.error(`[vuex] unknown mutation type: ${type}`)
}
return
}
this._withCommit(() => {
entry.forEach(function commitIterator (handler) {
handler(payload)
})
})
this._subscribers.forEach(sub => sub(mutation, this.state))
if (
process.env.NODE_ENV !== ‘production‘ &&
options && options.silent
) {
console.warn(
`[vuex] mutation type: ${type}. Silent option has been removed. ` +
‘Use the filter functionality in the vue-devtools‘
)
}
}
commit
,由于_mutations
存储的是所有的type
(包括模块的),这个commit可以commit(‘shop/card‘)
只要命名路径对_subscribers
,遍历该数组调用回调dispatch
dispatch (_type, _payload) {
// check object-style dispatch
const {
type,
payload
} = unifyObjectStyle(_type, _payload)
const action = { type, payload }
const entry = this._actions[type]
if (!entry) {
if (process.env.NODE_ENV !== ‘production‘) {
console.error(`[vuex] unknown action type: ${type}`)
}
return
}
this._actionSubscribers.forEach(sub => sub(action, this.state))
return entry.length > 1
? Promise.all(entry.map(handler => handler(payload)))
: entry[0](payload)
}
dispatch
,和commit
一样,_actions
存放的是所有的action的type
(包括模块的)_actionSubscribers
中订阅的回调函数_actions[type]
是一个数组,可以一个type
不同处理,也即_actions[type] = [fn1, fn2, fn3, ...]
Promise.all
等到所有函数都调用完才统一处理(支持异步)subscribe
subscribe (fn) {
return genericSubscribe(fn, this._subscribers)
}
commit
,只要调用全局的commit
就调用,模块内的context.commit
也是会调用全部的commit
subscribeAction
subscribeAction (fn) {
return genericSubscribe(fn, this._actionSubscribers)
}
action
,只要调用全局的action
就调用,模块内的context.dispatch
也是会调用全部的dispatch
watch
watch (getter, cb, options) {
if (process.env.NODE_ENV !== ‘production‘) {
assert(typeof getter === ‘function‘, `store.watch only accepts a function.`)
}
return this._watcherVM.$watch(() => getter(this.state, this.getters), cb, options)
}
Vue
的$watch
,观察想要监控的状态registerModule
unregisterModule
hotUpdate
registerModule
registerModule (path, rawModule, options = {}) {
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.`)
assert(path.length > 0, ‘cannot register the root module by using registerModule.‘)
}
this._modules.register(path, rawModule)
installModule(this, this.state, path, this._modules.get(path), options.preserveState)
// reset store to update getters...
resetStoreVM(this, this.state)
}
Store
和store._vm
installModule
和 resetStoreVM
这两个函数很总要,涉及到性能,重点、重点unregisterModule
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])
})
resetStore(this)
}
Vue.delete(parentState, path[path.length - 1])
和resetStore(this)
Store
和_vm
hotUpdate
hotUpdate (newOptions) {
this._modules.update(newOptions)
resetStore(this, true)
}
resetStore(this, true)
,还是会重新创建Store
和_vm
很重要的3个函数
resetStore
:包含resetStoreVM
和installModule
resetStoreVM
installModule
标签:local 匹配 aio product catch sse $watch 不同 string
原文地址:https://www.cnblogs.com/lantuoxie/p/9353789.html