vuex的源码实现

343 阅读3分钟

一、解决思路

  1. Vuex 的源码主要由几个核心部分组成,包括 Store 类、ModuleCollection 类、Module 类、install 方法等,我们可以逐步分析这些部分的实现逻辑。

  2. 先看 Store 类,它是 Vuex 的核心,负责存储状态、处理 mutationaction 和 getter 等。

  3. ModuleCollection 类用于收集和管理模块,将模块组织成树状结构。

  4. Module 类表示一个模块,包含自身的状态、mutationaction 和 getter 等信息。

  5. install 方法将 Vuex 作为 Vue 的插件进行安装。

二、核心代码分析

以下是 Vuex 源码的部分关键代码及解释:

1. Store 类(部分代码)

收起

javascript

class Store {
  constructor (options = {}) {
    // 初始化选项
    const {
      plugins = [],
      strict = false
    } = 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._watcherVM = new Vue();
    this._makeLocalGettersCache = Object.create(null);

    // 安装模块
    this._modulesNamespaceMap = Object.create(null);
    installModule(this, state, [], this._modules.root);

    // 初始化 Vue 实例,将状态添加到 Vue 实例的 data 中
    const computed = {};
    this._wrappedGetters.forEach((fn, key) => {
      computed[key] = () => fn(this.state);
    });
    this._vm = new Vue({
      data: {
        $$state: state
      },
      computed
    });

    // 调用插件
    plugins.forEach(plugin => plugin(this));
  }

  // 提交 mutation
  commit (_type, _payload, _options) {
    const {
      type,
      payload,
      options
    } = unifyObjectStyle(_type, _payload, _options);
    const entry = this._mutations[type];
    if (!entry) {
      console.error(`[vuex] unknown mutation type: ${type}`);
      return;
    }
    this._withCommit(() => {
      entry.forEach(function commitIterator (handler) {
        handler(payload);
      });
    });
    this._subscribers.forEach(sub => sub({ type, payload }, this.state));
  }

  // 分发 action
  dispatch (_type, _payload) {
    const {
      type,
      payload
    } = unifyObjectStyle(_type, _payload);
    const entry = this._actions[type];
    if (!entry) {
      console.error(`[vuex] unknown action type: ${type}`);
      return;
    }
    const result = entry.length > 1
     ? Promise.all(entry.map(handler => handler(payload)))
      : entry[0](payload);
    return result;
  }

  // 获取状态
  get state () {
    return this._vm._data.$$state;
  }
}

代码解释

  • Store 类的构造函数:

    • 初始化了许多内部属性,如 _actions_mutations_wrappedGetters 等,用于存储相关信息。
    • 使用 ModuleCollection 类收集和管理模块,将模块组织成树状结构。
    • 创建一个 Vue 实例 _vm,将状态存储在 Vue 实例的 data 中,利用 Vue 的响应式系统实现状态的响应式更新。
    • 调用插件,执行插件的安装逻辑。
  • commit 方法:

    • 查找对应的 mutation 并执行,通过 _withCommit 方法确保在修改状态时标记为正在提交,避免在非 mutation 中修改状态。
    • 调用订阅者的回调函数,通知订阅者 mutation 发生。
  • dispatch 方法:

    • 查找对应的 action 并执行,支持多个 action 的情况,返回一个 Promise 或 Promise 数组。
  • get state 方法:

    • 用于获取存储在 Vue 实例中的状态。

2. ModuleCollection 类(部分代码)

收起

javascript

class ModuleCollection {
  constructor (rawRootModule) {
    this.register([], rawRootModule);
  }

  register (path, rawModule) {
    const newModule = new Module(rawModule);
    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) {
      Object.keys(rawModule.modules).forEach(key => {
        this.register(path.concat(key), rawModule.modules[key]);
      });
    }
  }

  get (path) {
    return path.reduce((module, key) => {
      return module.getChild(key);
    }, this.root);
  }
}

代码解释

  • ModuleCollection 类的构造函数:

    • 调用 register 方法注册根模块。
  • register 方法:

    • 创建一个新的 Module 实例,并根据路径添加到相应的父模块中。
    • 如果模块有子模块,递归调用 register 方法注册子模块。
  • get 方法:

    • 根据路径查找模块。

3. Module 类(部分代码)

收起

javascript

class Module {
  constructor (rawModule) {
    this._rawModule = rawModule;
    this._children = Object.create(null);
    this.state = rawModule.state || {};
  }

  addChild (key, module) {
    this._children[key] = module;
  }

  getChild (key) {
    return this._children[key];
  }
}

代码解释

  • Module 类的构造函数:

    • 存储原始模块信息,初始化子模块对象和状态。
  • addChild 方法:

    • 添加子模块。
  • getChild 方法:

    • 获取子模块。

4. install 方法(部分代码)

收起

javascript

function install (_Vue) {
  if (Vue && _Vue === Vue) {
    if (process.env.NODE_ENV!== 'production') {
      console.error(
        '[vuex] already installed. Vue.use(Vuex) should be called only once.'
      );
      return;
    }
  } else {
    Vue = _Vue;
  }

  applyMixin(Vue);
}

代码解释

  • install 方法:

    • 检查 Vue 是否已经安装 Vuex,防止重复安装。

    • 调用 applyMixin 方法将 Vuex 作为 Vue 的插件进行安装。

三、使用说明

  1. Vuex 的源码实现了一个强大的状态管理模式,将状态存储在 Vue 实例中,利用 Vue 的响应式系统。

  2. Store 类负责管理状态、处理 mutationaction 和 getter 等操作,通过 ModuleCollection 管理模块。

  3. 模块以树状结构存储,方便管理和扩展。

  4. mutation 是同步操作,通过 commit 方法调用,action 可包含异步操作,通过 dispatch 方法调用。

四、注意事项

  1. 深入理解 Vue 的响应式系统对于理解 Vuex 的源码很有帮助,因为 Vuex 依赖 Vue 的响应式特性。

  2. Vuex 源码中的模块管理部分需要理解树状结构的组织和遍历。

  3. 在使用 Vuex 时,遵循其同步 mutation 和异步 action 的规则,确保状态管理的可预测性和可维护性。

你是在学习 Vuex 的源码,还是想对 Vuex 的实现进行扩展或优化呢 ?如果你对源码的某个部分还有疑问,我可以为你提供更详细的解释哦 。

请注意,上述代码只是 Vuex 源码的一部分,完整的 Vuex 源码包含更多细节和优化。如果你想查看完整的 Vuex 源码,可以在 Vuex 的官方 GitHub 仓库中找到。