本文章基于以下版本撰写
在 vue 中使用 vuex
import { createApp } from 'vue'
import { createStore } from 'vuex'
const store = createStore({
state () {
return {
count: 0
}
},
mutations: {
increment (state) {
state.count++
}
}
})
const app = createApp({ })
app.use(store)
从 createStore 引入讲起
让我们先看一下 vuex/src 目录,引入 createStore 就是从 index.js 文件中引入
??import { Store, createStore } from './store'
import { storeKey, useStore } from './injectKey'
import { mapState, mapMutations, mapGetters, mapActions, createNamespacedHelpers } from './helpers'
import { createLogger } from './plugins/logger'
export default {
version: '__VERSION__',
Store,
storeKey,
??createStore,
useStore,
mapState,
mapMutations,
mapGetters,
mapActions,
createNamespacedHelpers,
createLogger
}
export {
Store,
storeKey,
createStore,
useStore,
mapState,
mapMutations,
mapGetters,
mapActions,
createNamespacedHelpers,
createLogger
}
可以看到 createStore 是从 store 文件中引用,那么下面我们来看下 store 文件
export function createStore(options) {
return new Store(options)
}
直接返回了一个 Store 的实例,所以我们知道 Store 肯定是一个 class,下面我们来看下 Store 类
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))
}
????????install (app, injectKey) {
app.provide(injectKey || storeKey, this)
app.config.globalProperties.$store = this
}
}
我们先看下 constructor 部分,我们知道通过 new 命令生成对象实例时,自动调用该方法。 1. 所以首先是存储内部状态 2. 在实例自身身上挂两个方法分别是原型上的dispatch 、commit方法,并将函数内部的this指针强行指向当前创建的store对象。 3. 初始化根Moudule 4. 对State进行响应式处理 5. 应用插件 6. 全局注册
?? ModuleCollection 模块处理
我们从 this._modules = new ModuleCollection(options) 创建实例对象讲起,从字面意思可知是一个 module 收集的过程
export default class ModuleCollection {
constructor (rawRootModule) {
this.register([], rawRootModule, false)
}
可以看到是调用 register 方法
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)
})
}
}
创建 Module 实例,先挂在到 root 属性上,然后看有没有 modules 属性,有的话就递归,给每个模块都创建一个 Module 实例对象,
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) || {}
}
}
这样每个模块就有自己单独的 state,实现独立管理自己模块的状态
???? 初始化根 Moudle installModule
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)
})
}
??: 若module.namespaced = true : 此Module将被加入store._modulesNamespaceMap内,其key为Module嵌套的路径。
????: 非root Module时:子Module.state注入到父节点的state对象里。
??????: 对store进行局部化,这里主要对module.namespaced= true 的module进行另外处理,其内部的成员都需要进行namespace路径处理处理。
????????: 对 mutation、action、getter 进行封装,放在 Store 对应的 _mutations、_actions、_wrappedGetters 里
??????????: 若当前module含有子module时,遍历当前model的_children属性,迭代执行installModule。
??????对State进行响应式处理 resetStoreState
export function resetStoreState (store, state, hot) {
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()
}
}
??: 将 getter 注册为计算属性
????: 让 state 变为响应式对象
???????? Store 全局注册
我们知道当我们应用插件 app.use(store) 时候,会自动调用 install 方法
4.0版本 vuex 使用的是 provide / inject 来实现全局注册,因为 vue3 已经不支持 $ api
install (app, injectKey) {
app.provide(injectKey || storeKey, this)
app.config.globalProperties.$store = this
}
除了通过 app.config.globalProperties 设置全局属性$store,还provide了一个storeKey,这显然是为 useStore() 做准备。
import { inject } from 'vue'
export const storeKey = 'store'
export function useStore (key = null) {
return inject(key !== null ? key : storeKey)
}
|