从浅入深vuex

89 阅读2分钟

定义(看下Copilot的回答)

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。Vuex 被设计用于中大型单页应用程序(SPA),为不同组件间的共享状态提供了一种统一的方式。

核心概念

  1. State(状态):

    • Vuex 使用单一状态树 —— 一个对象包含了全部的应用层级状态。作为一个“唯一数据源(SSOT)”,它使得状态在应用中保持一致。
  2. Getters(获取器):

    • 类似于组件的计算属性。当你需要从 store 的 state 中派生出一些状态时,可以使用 getters。
  3. Mutations(变更):

    • 更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。Vuex 中的 mutations 非常类似于事件:每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数。
  4. Actions(动作):

    • Actions 类似于 mutations,不同之处在于:
      • Action 提交的是 mutation,而不是直接变更状态。
      • Action 可以包含任意异步操作。
  5. Modules(模块):

    • 由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就可能变得相当臃肿。为了解决这个问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter 甚至是嵌套子模块。

工作流程

  1. 组件内部通过 dispatch 方法触发 actions。
  2. Actions 内部可以执行异步操作,然后通过 commit 方法触发 mutations。
  3. Mutations 修改 state,Vue 组件会自动响应状态变化。

使用场景

  • 多个视图依赖于同一状态。
  • 来自不同视图的行为需要变更同一状态。

Vuex 提供了一种机制将组件的共享状态外提到一个全局单例中管理,使得你可以在应用中的任何位置访问或修改状态。

源码分析

  1. vue使用vuex
// 实例化vuex 
const store = createStore({
  // 两个模块
  modules: {
    cart,
    products
  },
  strict: debug,
  // 日志记录器的工厂函数 提供了灵活的日志记录能力,允许开发者根据需要配置日志的详细程度和格式
  // 非常适合在开发过程中跟踪和调试状态管理相关的问题
  plugins: debug ? [createLogger()] : []
})
export default store;
// vue.use安装插件,store对象含有install方法
vue.use(store)
// store类的install方法
install (app, injectKey) {
    // 将store对象注入到所有的子组件中
    app.provide(injectKey || storeKey, this)
    app.config.globalProperties.$store = this
}
  1. vuex的实例化
export function createStore (options) {
  return new Store(options)
}

export class Store {
  constructor (options = {}) {
      // 设置和管理vuex的模块集合
      this._modules = new ModuleCollection(options)
      // 初始化根模块
      // 这也递归地注册所有子模块
      // 收集this. _wrappegetters中的所有模块getter
      installModule(this, state, [], this._modules.root)

      // 初始化负责响应性的存储状态
      // 同时注册_wrappegetters作为计算属性
      resetStoreState(this, state)

      // apply plugins
      plugins.forEach(plugin => plugin(this))
  }
}
  1. ModuleCollection是一个vuex store设置和管理所有的模块的类
export default class ModuleCollection {
  // rawRootModule 实例化vuex时的options
  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)
  }

  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)
    }

    // register nested modules
    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
  }
}