本人阅读vuex源码主要想了解的问题
- vuex的单项数据流的调用流程是什么?
- 如何与vue结合实现数据改变后的响应式更新?
如下基于vuex 4.1.0版本
管理配置项
传递给vuex的配置项目会生成一个module类进行管理,如果配置项中存在modules则会继续生成对应的module,整个过程是通过递归处理的
import { forEachValue } from '../util'
export default class Module {
constructor (rawModule, runtime) {
this.runtime = runtime
this._children = Object.create(null)
this._rawModule = rawModule
const rawState = rawModule.state
this.state = (typeof rawState === 'function' ? rawState() : rawState) || {}
}
get namespaced () {
return !!this._rawModule.namespaced
}
addChild (key, module) {
this._children[key] = module
}
removeChild (key) {
delete this._children[key]
}
getChild (key) {
return this._children[key]
}
hasChild (key) {
return key in this._children
}
update (rawModule) {
this._rawModule.namespaced = rawModule.namespaced
if (rawModule.actions) {
this._rawModule.actions = rawModule.actions
}
if (rawModule.mutations) {
this._rawModule.mutations = rawModule.mutations
}
if (rawModule.getters) {
this._rawModule.getters = rawModule.getters
}
}
forEachChild (fn) {
forEachValue(this._children, fn)
}
forEachGetter (fn) {
if (this._rawModule.getters) {
forEachValue(this._rawModule.getters, fn)
}
}
forEachAction (fn) {
if (this._rawModule.actions) {
forEachValue(this._rawModule.actions, fn)
}
}
forEachMutation (fn) {
if (this._rawModule.mutations) {
forEachValue(this._rawModule.mutations, fn)
}
}
}
-
module-collection管理所有module
根据传递的配置项,生成对应的module,如果有modules,则会递归处理生成对应的子module
该类用来注册、管理所有的module
import Module from './module'
import { assert, forEachValue } from '../util'
export default class ModuleCollection {
constructor (rawRootModule) {
this.register([], rawRootModule, false)
}
get (path) {
return path.reduce((module, key) => {
return module.getChild(key)
}, this.root)
}
getNamespace (path) {
let module = this.root
return path.reduce((namespace, key) => {
module = module.getChild(key)
return namespace + (module.namespaced ? key + '/' : '')
}, '')
}
update (rawRootModule) {
update([], this.root, rawRootModule)
}
register (path, rawModule, runtime = true) {
if (__DEV__) {
assertRawModule(path, rawModule)
}
const newModule = new Module(rawModule, runtime)
if (path.length === 0) {
this.root = newModule
} else {
const parent = this.get(path.slice(0, -1))
parent.addChild(path[path.length - 1], newModule)
}
if (rawModule.modules) {
forEachValue(rawModule.modules, (rawChildModule, key) => {
this.register(path.concat(key), rawChildModule, runtime)
})
}
}
unregister (path) {
const parent = this.get(path.slice(0, -1))
const key = path[path.length - 1]
const child = parent.getChild(key)
if (!child) {
if (__DEV__) {
console.warn(
`[vuex] trying to unregister module '${key}', which is ` +
`not registered`
)
}
return
}
if (!child.runtime) {
return
}
parent.removeChild(key)
}
isRegistered (path) {
const parent = this.get(path.slice(0, -1))
const key = path[path.length - 1]
if (parent) {
return parent.hasChild(key)
}
return false
}
}
注册流程
使用者调用createStore时会实例化store,store便是可以直接交给外部调用的
export function createStore (options) {
return new Store(options)
}
Store
import ModuleCollection from './module/module-collection'
export class Store {
constructor (options = {}) {
if (__DEV__) {
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,
devtools
} = options
this._modules = new ModuleCollection(options)
}
}
总结一下:到这里可以知道,整个交给vuex的内容会被转换成一个个module,根据层级关系进行挂载,整个Vuex是一个大的对象
-
注册state、mutations、getters、actions
这里先总结一下后文经常出现的变量:
- path:保存module命名路径的数组,比如b模块:[‘a’,‘b’]
- namespace:保存模块的命名空间路径,是个字符串,比如b模块:‘a/b/’
export class Store {
constructor (options = {}) {
if (__DEV__) {
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,
devtools
} = options
this._committing = false
this._actions = Object.create(null)
this._mutations = Object.create(null)
this._wrappedGetters = Object.create(null)
this._makeLocalGettersCache = Object.create(null)
this._modulesNamespaceMap = Object.create(null)
this._modules = new ModuleCollection(options)
const state = this._modules.root.state
installModule(this, state, [], this._modules.root)
}
installModule
- 递归注册所有配置项
- 将所有module的state统一保存在根module的state上,通过module的name进行区分
- 注册mutations等方法
- 为所有声明了namespaced: true的module创建一个局部下文,通过这个局部上下文调用module的内容(actions等),会自动拼接上该方法等命名空间路径
export function installModule (store, rootState, path, module, hot) {
const isRoot = !path.length
const namespace = store._modules.getNamespace(path)
if (module.namespaced) {
if (store._modulesNamespaceMap[namespace] && __DEV__) {
console.error(`[vuex] duplicate namespace ${namespace} for the namespaced module ${path.join('/')}`)
}
store._modulesNamespaceMap[namespace] = module
}
if (!isRoot && !hot) {
const parentState = getNestedState(rootState, path.slice(0, -1))
const moduleName = path[path.length - 1]
store._withCommit(() => {
if (__DEV__) {
if (moduleName in parentState) {
console.warn(
`[vuex] state field "${moduleName}" was overridden by a module with the same name at "${path.join('.')}"`
)
}
}
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)
})
}
getNestedState
- 所有module的state也是保存在一个对象中,通过module的name进行挂载,{…,moduleA:{…,moduleB:…}}
export function getNestedState (state, path) {
return path.reduce((state, key) => state[key], state)
}
注册mutations、actions等配置
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)
})
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)
})
}
function registerAction (store, type, handler, local) {
const entry = store._actions[type] || (store._actions[type] = [])
entry.push(function wrappedActionHandler (payload) {
let res = handler.call(store, {
dispatch: local.dispatch,
commit: local.commit,
getters: local.getters,
state: local.state,
rootGetters: store.getters,
rootState: store.state
}, payload)
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
}
})
}
function registerGetter (store, type, rawGetter, local) {
if (store._wrappedGetters[type]) {
if (__DEV__) {
console.error(`[vuex] duplicate getter key: ${type}`)
}
return
}
store._wrappedGetters[type] = function wrappedGetter (store) {
return rawGetter(
local.state,
local.getters,
store.state,
store.getters
)
}
}
调用
store.commit
export class Store {
commit (_type, _payload, _options) {
const {
type,
payload,
options
} = unifyObjectStyle(_type, _payload, _options)
const mutation = { type, payload }
const entry = this._mutations[type]
if (!entry) {
if (__DEV__) {
console.error(`[vuex] unknown mutation type: ${type}`)
}
return
}
this._withCommit(() => {
entry.forEach(function commitIterator (handler) {
handler(payload)
})
})
this._subscribers
.slice()
.forEach(sub => sub(mutation, this.state))
if (
__DEV__ &&
options && options.silent
) {
console.warn(
`[vuex] mutation type: ${type}. Silent option has been removed. ` +
'Use the filter functionality in the vue-devtools'
)
}
}
}
store.dispatch
dispatch (_type, _payload) {
const {
type,
payload
} = unifyObjectStyle(_type, _payload)
const action = { type, payload }
const entry = this._actions[type]
if (!entry) {
if (__DEV__) {
console.error(`[vuex] unknown action type: ${type}`)
}
return
}
try {
this._actionSubscribers
.slice()
.filter(sub => sub.before)
.forEach(sub => sub.before(action, this.state))
} catch (e) {
if (__DEV__) {
console.warn(`[vuex] error in before action subscribers: `)
console.error(e)
}
}
const result = entry.length > 1
? Promise.all(entry.map(handler => handler(payload)))
: entry[0](payload)
return new Promise((resolve, reject) => {
result.then(res => {
try {
this._actionSubscribers
.filter(sub => sub.after)
.forEach(sub => sub.after(action, this.state))
} catch (e) {
if (__DEV__) {
console.warn(`[vuex] error in after action subscribers: `)
console.error(e)
}
}
resolve(res)
}, error => {
try {
this._actionSubscribers
.filter(sub => sub.error)
.forEach(sub => sub.error(action, this.state, error))
} catch (e) {
if (__DEV__) {
console.warn(`[vuex] error in error action subscribers: `)
console.error(e)
}
}
reject(error)
})
})
}
store.getters
export function resetStoreState (store, state, hot, reserve = true) {
const oldState = store._state
const oldScope = store._scope
store.getters = {}
store._makeLocalGettersCache = Object.create(null)
const wrappedGetters = store._wrappedGetters
const computedObj = {}
const computedCache = {}
const scope = effectScope(true)
scope.run(() => {
forEachValue(wrappedGetters, (fn, key) => {
computedObj[key] = partial(fn, store)
computedCache[key] = computed(() => computedObj[key]())
Object.defineProperty(store.getters, key, {
get: () => computedCache[key].value,
enumerable: true
})
})
})
}
export function partial (fn, arg) {
return function () {
return fn(arg)
}
}
store.state
export function resetStoreState (store, state, hot, reserve = true) {
const oldState = store._state
const oldScope = store._scope
const scope = effectScope(true)
scope.run(() => {
forEachValue(wrappedGetters, (fn, key) => {
})
})
store._state = reactive({
data: state
})
store._scope = scope
if (oldState) {
if (hot) {
store._withCommit(() => {
oldState.data = null
})
}
}
}
export class Store {
get state () {
return this._state.data
}
}
每个单独module的调用会在installModule里通过makeLocalContext进行劫持
- 主要作用是为每个module调用内容的key拼接上命名空间
export function installModule (store, rootState, path, module, hot) {
const isRoot = !path.length
const namespace = store._modules.getNamespace(path)
const local = module.context = makeLocalContext(store, namespace, path)
module.forEachMutation((mutation, key) => {
const namespacedType = namespace + key
registerMutation(store, namespacedType, mutation, local)
})
}
- actions、mutations、state
- 进行命名空间的拼接,最终还是调用挂在store上的全局commit、dispatch
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 (__DEV__ && !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 (__DEV__ && !store._mutations[type]) {
console.error(`[vuex] unknown local mutation type: ${args.type}, global type: ${type}`)
return
}
}
store.commit(type, payload, options)
}
}
Object.defineProperties(local, {
getters: {
get: noNamespace
? () => store.getters
: () => makeLocalGetters(store, namespace)
},
state: {
get: () => getNestedState(store.state, path)
}
})
return local
}
- getters
- 对于getters的调用则比较复杂
- 如果module没有命名空间,则直接调用store.getter上的对应方法
- 注意getters在installModule中通过registerGetter注册到了store._wrappedGetters上,然后又在resetStoreState中对store.getters进行了劫持处理,访问store.getter其实就是访问store._wrappedGetters
- 对于局部getters,则会通过makeLocalGetters再次进行拦截,主要作用是帮助拼接命名空间路径,然后拦截指向store.getters
makeLocalGetters
export function makeLocalGetters (store, namespace) {
if (!store._makeLocalGettersCache[namespace]) {
const gettersProxy = {}
const splitPos = namespace.length
Object.keys(store.getters).forEach(type => {
if (type.slice(0, splitPos) !== namespace) return
const localType = type.slice(splitPos)
Object.defineProperty(gettersProxy, localType, {
get: () => store.getters[type],
enumerable: true
})
})
store._makeLocalGettersCache[namespace] = gettersProxy
}
return store._makeLocalGettersCache[namespace]
}
- 总结一下,其实到这里可以知道(vuex就是一个究极大的对象):
- vuex的所有state会挂载到根module的state上,具有上下级的层级结构(一个大的对象)
- vuex的所有getters、mutations、actions也会都放到一个大的对象上,没有层级结构
实现响应式
在注册流程结束后,调用resetStoreState将状态变成响应式
- state通过reactive
- getters通过computed
export class Store {
constructor (options = {}) {
if (__DEV__) {
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,
devtools
} = options
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._makeLocalGettersCache = Object.create(null)
this._scope = null
this._devtools = devtools
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)
}
this.strict = strict
const state = this._modules.root.state
installModule(this, state, [], this._modules.root)
resetStoreState(this, state)
plugins.forEach(plugin => plugin(this))
}
}
resetStoreState
- 对state、getters进行响应式处理
- 通过 effectScope 避免计算属性随着组件卸载而失去响应性
- 因为组件中的setup实际也是运行在一个effectScope中,当组建卸载时会自动卸载掉所有依赖
- 通过effectScope(true)传递true参数,避免父effectScope影响子effectScope
export function resetStoreState (store, state, hot, reserve = true) {
const oldState = store._state
const oldScope = store._scope
store.getters = {}
store._makeLocalGettersCache = Object.create(null)
const wrappedGetters = store._wrappedGetters
const computedObj = {}
const computedCache = {}
const scope = effectScope(true)
scope.run(() => {
forEachValue(wrappedGetters, (fn, key) => {
computedObj[key] = partial(fn, store)
computedCache[key] = computed(() => computedObj[key]())
Object.defineProperty(store.getters, key, {
get: () => computedCache[key].value,
enumerable: true
})
})
})
store._state = reactive({
data: state
})
store._scope = scope
if (store.strict) {
enableStrictMode(store)
}
if (oldState) {
if (hot) {
store._withCommit(() => {
oldState.data = null
})
}
}
if (oldScope) {
oldScope.stop()
}
}
注册进业务组件
在业务组件中通过this就能访问到store
install (app, injectKey) {
app.provide(injectKey || storeKey, this)
app.config.globalProperties.$store = this
const useDevtools = this._devtools !== undefined
? this._devtools
: __DEV__ || __VUE_PROD_DEVTOOLS__
if (useDevtools) {
addDevtools(app, this)
}
}
热更新
- hotUpdate
- 当热更新时,需要将新的配置内容替换旧的,通过ModuleCollection的update进行更新
hotUpdate (newOptions) {
this._modules.update(newOptions)
resetStore(this, true)
}
- 会递归调用update方法,将所有module的配置项进行更新
ModuleCollection {
update (rawRootModule) {
update([], this.root, rawRootModule)
}
}
function update (path, targetModule, newModule) {
if (__DEV__) {
assertRawModule(path, newModule)
}
targetModule.update(newModule)
if (newModule.modules) {
for (const key in newModule.modules) {
if (!targetModule.getChild(key)) {
if (__DEV__) {
console.warn(
`[vuex] trying to add a new module '${key}' on hot reloading, ` +
'manual reload is needed'
)
}
return
}
update(
path.concat(key),
targetModule.getChild(key),
newModule.modules[key]
)
}
}
}
update (rawModule) {
this._rawModule.namespaced = rawModule.namespaced
if (rawModule.actions) {
this._rawModule.actions = rawModule.actions
}
if (rawModule.mutations) {
this._rawModule.mutations = rawModule.mutations
}
if (rawModule.getters) {
this._rawModule.getters = rawModule.getters
}
}
resetStore
export 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
installModule(store, state, [], store._modules.root, true)
resetStoreState(store, state, hot, false)
}
4.1.0版本问题
-
解决方案 - 其实在4.1.0版本时,当通过vuex api进行添加module时,会使得计算属性丢失响应,原因是resetStoreState中会停止掉旧的scope所有依赖
registerModule (path, rawModule, options = {}) {
if (typeof path === 'string') path = [path]
if (__DEV__) {
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)
resetStoreState(this, this.state)
}
- 解决方式是将旧scope的依赖添加进新的scope中
export function resetStoreState (store, state, hot, reserve = true) {
const oldState = store._state
const oldScope = store._scope
if (oldState) {
if (hot) {
store._withCommit(() => {
oldState.data = null
})
}
}
if (oldScope) {
if (reserve) {
const deadEffects = []
oldScope.effects.forEach(effect => {
if (effect.deps.length) {
scope.effects.push(effect)
} else {
deadEffects.push(effect)
}
})
oldScope.effects = deadEffects
}
oldScope.stop()
}
}
- 当然如果添加了上一步修改,那么还需要处理通过vuex api进行卸载module时,将旧的effects卸载掉
unregisterModule (path) {
if (typeof path === 'string') path = [path]
if (__DEV__) {
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))
delete parentState[path[path.length - 1]]
})
resetStore(this)
}
export 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
installModule(store, state, [], store._modules.root, true)
resetStoreState(store, state, hot, false)
}
辅助函数
normalizeNamespace
...mapActions([
'some/nested/module/foo',
'some/nested/module/bar'
])
...mapActions('some/nested/module', [
'foo',
'bar'
])
function normalizeNamespace (fn) {
return (namespace, map) => {
if (typeof namespace !== 'string') {
map = namespace
namespace = ''
} else if (namespace.charAt(namespace.length - 1) !== '/') {
namespace += '/'
}
return fn(namespace, map)
}
}
normalizeMap
- 处理通过MapXXX辅助函数调用时,传递的对象的不同格式
...mapState({
a: state => state.a,
b: state => state.b
})
...mapActions([
'foo',
'bar'
])
function normalizeMap (map) {
if (!isValidMap(map)) {
return []
}
return Array.isArray(map)
? map.map(key => ({ key, val: key }))
: Object.keys(map).map(key => ({ key, val: map[key] }))
}
getModuleByNamespace
function getModuleByNamespace (store, helper, namespace) {
const module = store._modulesNamespaceMap[namespace]
if (__DEV__ && !module) {
console.error(`[vuex] module namespace not found in ${helper}(): ${namespace}`)
}
return module
}
export const mapState = normalizeNamespace((namespace, states) => {
const res = {}
if (__DEV__ && !isValidMap(states)) {
console.error('[vuex] mapState: mapper parameter must be either an Array or an Object')
}
normalizeMap(states).forEach(({ key, val }) => {
res[key] = function mappedState () {
let state = this.$store.state
let getters = this.$store.getters
if (namespace) {
const module = getModuleByNamespace(this.$store, 'mapState', namespace)
if (!module) {
return
}
state = module.context.state
getters = module.context.getters
}
return typeof val === 'function'
? val.call(this, state, getters)
: state[val]
}
res[key].vuex = true
})
return res
})
-
mapGetters
- 对于getters,直接从store.getters上拿,因为getters不需要传递参数
export const mapGetters = normalizeNamespace((namespace, getters) => {
const res = {}
if (__DEV__ && !isValidMap(getters)) {
console.error('[vuex] mapGetters: mapper parameter must be either an Array or an Object')
}
normalizeMap(getters).forEach(({ key, val }) => {
val = namespace + val
res[key] = function mappedGetter () {
if (namespace && !getModuleByNamespace(this.$store, 'mapGetters', namespace)) {
return
}
if (__DEV__ && !(val in this.$store.getters)) {
console.error(`[vuex] unknown getter: ${val}`)
return
}
return this.$store.getters[val]
}
res[key].vuex = true
})
return res
})
其他mapMutations、mapActions类似,就不做分析了
createNamespacedHelpers
- 将所有mapHelper绑定一个统一的命名空间,简化调用
export const createNamespacedHelpers = (namespace) => ({
mapState: mapState.bind(null, namespace),
mapGetters: mapGetters.bind(null, namespace),
mapMutations: mapMutations.bind(null, namespace),
mapActions: mapActions.bind(null, namespace)
})
其他的一些utils
在vuex中修改state都必须使用_withCommit,通过watch api来监测直接修改state
_withCommit (fn) {
const committing = this._committing
this._committing = true
fn()
this._committing = committing
}
function enableStrictMode (store) {
watch(() => store._state.data, () => {
if (__DEV__) {
assert(store._committing, `do not mutate vuex store state outside mutation handlers.`)
}
}, { deep: true, flush: 'sync' })
}
export function assert (condition, msg) {
if (!condition) throw new Error(`[vuex] ${msg}`)
}
export function isPromise (val) {
return val && typeof val.then === 'function'
}
export function isObject (obj) {
return obj !== null && typeof obj === 'object'
}
export function forEachValue (obj, fn) {
Object.keys(obj).forEach(key => fn(obj[key], key))
}
和3.x版本的区别
-
响应式区别
- vuex 3.x的版本对应vue 2.x,所以在响应式方面并不能使用单独的reactive等api
export class Store {
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)
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)
}
this.strict = strict
const state = this._modules.root.state
installModule(this, state, [], this._modules.root)
resetStoreVM(this, state)
}
resetStoreVM
- 3.x版本响应式的核心是生成了一个单独的vue实例,vuex将数据挂载到该vue实例上
function resetStoreVM (store, state, hot) {
const oldVm = store._vm
store.getters = {}
store._makeLocalGettersCache = Object.create(null)
const wrappedGetters = store._wrappedGetters
const computed = {}
forEachValue(wrappedGetters, (fn, key) => {
computed[key] = partial(fn, store)
Object.defineProperty(store.getters, key, {
get: () => store._vm[key],
enumerable: true
})
})
const silent = Vue.config.silent
Vue.config.silent = true
store._vm = new Vue({
data: {
$$state: state
},
computed
})
Vue.config.silent = silent
if (store.strict) {
enableStrictMode(store)
}
if (oldVm) {
if (hot) {
store._withCommit(() => {
oldVm._data.$$state = null
})
}
Vue.nextTick(() => oldVm.$destroy())
}
}
export class Store {
get state () {
return this._vm._data.$$state
}
}
-
注册进组件区别
- 在4.1.0版本中,vuex是通过app.config.globalProperties将store绑定到所有实例上
install (app, injectKey) {
app.provide(injectKey || storeKey, this)
app.config.globalProperties.$store = this
const useDevtools = this._devtools !== undefined
? this._devtools
: __DEV__ || __VUE_PROD_DEVTOOLS__
if (useDevtools) {
addDevtools(app, this)
}
}
new Vue({
el: '#app',
store: store,
})
export default function (Vue) {
const version = Number(Vue.version.split('.')[0])
if (version >= 2) {
Vue.mixin({ beforeCreate: vuexInit })
}
function vuexInit () {
const options = this.$options
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
}
}
}
|