在探究怎么实现之前,先看下文档中是怎么使用的
// 创建 自己项目中的 store
import { createStore } from 'vuex'
export default createStore({
})
// 在自己的vue 项目中 使用 store
import { createApp } from 'vue'
createApp(App).use(store).mount('#app')
上面的使用过程, 我们可以看到 ,创建了 vue 实例后 ,把创建的 store 作为参数调用了 use 方法。下面来看下源码
-
在vue 的源码中创建实例 createStore packages\runtime-core\src\apiCreateApp.ts
export function createAppAPI<HostElement>( render: RootRenderFunction, hydrate?: RootHydrateFunction ): CreateAppFunction<HostElement> { return function createApp(rootComponent, rootProps = null) { if (rootProps != null && !isObject(rootProps)) { __DEV__ && warn(`root props passed to app.mount() must be an object.`) rootProps = null } const context = createAppContext() // 利用 set 来存储使用的插件,防止重复 const installedPlugins = new Set() let isMounted = false const app: App = (context.app = { .... // 调用use 将使用的插件和vue 联系在一起 use(plugin: Plugin, ...options: any[]) { if (installedPlugins.has(plugin)) { __DEV__ && warn(`Plugin has already been applied to target app.`) } else if (plugin && isFunction(plugin.install)) { // store 中有 install 所以走的是这里 // 将传入的插件 放在set 中 installedPlugins.add(plugin) // 调用插件的install ,将当前app 也就是this 传入插件中 plugin.install(app, ...options) } else if (isFunction(plugin)) { installedPlugins.add(plugin) plugin(app, ...options) } else if (__DEV__) { warn( `A plugin must either be a function or an object with an "install" ` + `function.` ) } return app }, ..... provide(key, value) { if (__DEV__ && (key as string | symbol) in context.provides) { warn( `App already provides property with key "${String(key)}". ` + `It will be overwritten with the new value.` ) } // TypeScript doesn't allow symbols as index type // https://github.com/Microsoft/TypeScript/issues/24587 // 根据插件传过来的 key 值,将插件实例存放在 vue 的 provides下面 context.provides[key as string] = value return app } }) return app } }下面是 vuex 创建的源码
export function createStore (options) { return new Store(options) } export class Store { ... install (app, injectKey) { // vue 调用 该方法,将当前实例 挂载到vue 对象中 // 调用 vue 的provide 函数 将当前实例 和要存储的key 值 传递过去 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) } } .... }
从上面代码中可以发现, 首先是vue 调用了use 方法, 在use 方法中传入了创建的 Store 的实例,实例会调用自身下面的install 方法。在install 方法中调用 vue 的provide函数, 将当前实例保存到vue 实例的provides 属性下。在vue 组件中可以导出 useStore方法 获取store ,就是从vue 实例的 provides 属性中获取
import { inject } from 'vue'
export const storeKey = 'store'
export function useStore (key = null) {
return inject(key !== null ? key : storeKey)
}
以上是 useStore实现的函数 inject 的实现 在 vue 的项目目录: runtime-core/src/apiinject.ts
下面再接着看下Store 类是怎么将传入的 值存放的
this._modules = new ModuleCollection(options)
在Store 的构造函数中 调用了上面的方法来将state 整理存放。返回值的结构如下
{
root: {
state: {},
_children: [
key: {
state: {},
_children:{}
}
]
}
}
vuex 中定义的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
// store internal state
this._committing = false
this._actions = Object.create(null)
this._actionSubscribers = []
this._mutations = Object.create(null)
this._wrappedGetters = Object.create(null)
// 在这里初始化了 options中的state
this._modules = new ModuleCollection(options)
this._modulesNamespaceMap = Object.create(null)
this._subscribers = []
this._makeLocalGettersCache = Object.create(null)
// EffectScope instance. when registering new getters, we wrap them inside
// EffectScope so that getters (computed) would not be destroyed on
// component unmount.
this._scope = null
this._devtools = devtools
// 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
// 处理后的state 存放的地方
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 state, which is responsible for the reactivity
// (also registers _wrappedGetters as computed properties)
resetStoreState(this, state)
// apply plugins
plugins.forEach(plugin => plugin(this))
}
}
简略的源码如下
export default class ModuleCollection {
constructor (rawRootModule) {
// register root module (Vuex.Store options)
this.register([], rawRootModule, false)
}
get (path) {
// 如果数组是空, 返回值是 初始值
return path.reduce((module, key) => {
return module.getChild(key)
}, this.root)
}
register (path, rawModule, runtime = true) {
if (__DEV__) {
assertRawModule(path, rawModule)
}
// 处理 options 中的state
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)
}
// register nested modules
// 递归调用该方法, 处理modules 中的state
if (rawModule.modules) {
forEachValue(rawModule.modules, (rawChildModule, key) => {
this.register(path.concat(key), rawChildModule, runtime)
})
}
}
}
export default class Module {
constructor (rawModule, runtime) {
this.runtime = runtime
// Store some children item
this._children = Object.create(null)
// Store the origin module object which passed by programmer
this._rawModule = rawModule
const rawState = rawModule.state
// Store the origin module's 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]
}
}
\