vue 3.0 使用vuex4.0 的实现过程

224 阅读3分钟

在探究怎么实现之前,先看下文档中是怎么使用的

// 创建 自己项目中的 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]
  }
 }

\