手写 Vuex 4.x

Vuex是官方提供的状态管理库,为了深入了解它的实现逻辑,本文我们一起来一步步实现它。

初始化

用法
// 1. 引入createStore函数
import { createStore } from "vuex";

// 2. 通过创建一个新的 store 实例
const store = createStore({

});

// 3. 将 store 实例作为插件安装
createApp(App).use(store).mount('#app')

代码实现
// 创建一个生成Store的
export function createStore() {
  return new Store();
}

// 把所有的功能放在 Store 实例中
export class Store {
  
  constructor() {}

  // 能做为插件,会调用install方法
  install(app, ...options) {
    
  }
}

state

用法
const store = createStore({
  // 1. 传入state
  state: {
    count: 2,
  }
});
<!-- 使用的组件 -->
<template>
  <div>
    <!-- 5. 模板中使用 -->
    {{ count }}
    <!-- 或者 -->
    {{ $store.state.count }}
  </div>
</template>

<script lang="ts">
import { defineComponent } from "vue";
// 2. 引入useStore函数
import { useStore } from 'vuex';

export default defineComponent({
  name: "App",
  setup() {

    // 3. 获取store
    const store = useStore();

    return {
      // 4. 响应式的count
      count: computed(() => store.state.count),
    };
  },
});
</script>
代码实现

思路:

  1. 通过Provide/Inject的技术实现每个组件能够通过useStore方法获取到store实例;
  2. 可以将store实例挂载到app.config.globalProperties.$store上,这样通过$store也可以取到store实例;
  3. state数据应该封装成响应式;
// 定一个key, 作为 provide/inject 的 key
const storeKey = "store";

export function createStore(options) {
  return new Store(options);
}

export class Store {
  
  constructor(options) {
    let _this = this;
    
    // 取到state并且变成响应式的数据, 加data这次是修改state方便
    this._state = reactive({data: options.state || Object.create(null)});

  }

  // 属性访问器进行数据封装,取的是_state.data
  get state() {
    return this._state.data;
  }

  install(app, ...options) {
    
    // 通过Provide注入给所有组件
    app.provide(storeKey, this);

    // 挂载在全局对象上,可通过this.$store取到
    app.config.globalProperties.$store = this;

  }

}

// 返回注入的store对象
export function useStore() {
  return inject(storeKey);
}

getters

用法
export default createStore({
  getters: {
    plusOne: (state) => state.count + 1,
  }
});
<template>
  <div>
    <!-- 使用数据 -->
    {{ plusOne }}
    {{ $store.getters.plusOne }}
  </div>
</template>

<script lang="ts">
import { computed, defineComponent } from "vue";
import { useStore } from "vuex";

export default defineComponent({
  name: "App",
  setup() {

    const store = useStore();

    return {
      // 获取 getter 数据
      plusOne: computed(() => store.getters.plusOne),
    };
  }
});
</script>
代码实现

思路:
使用的时候是用属性取值,而定义的时候是函数,所以需要将 函数调用 变成 获取属性

export class Store {
  
  constructor(options) {
    
    // 将getters函数转成属性
    this.getters = {};
    // this.getters = {plusOne: }
    foreachValue(options.getters, (value, key) => {
      // value 是 一个函数
      Object.defineProperty(this.getters, key, {
        // getters 都是基于 state, 所以传入this.state
        // 获取值的时候本质上还是函数调用
        get: () => value(_this.state)
      })
    });

  }

}
  • 工具函数
/**
 * 遍历对象属性执行函数
 * @param {*} obj 需要遍历的对象
 * @param {*} fn 对象的每个key值, 执行函数,传参为 value 和 key
 */
export function foreachValue(obj, fn) {
  if (isObject(obj)) {
    Object.keys(obj).forEach(key => fn(obj[key], key));
  }
}

/**
 *
 * @param {*} obj 传入参数
 * @returns 是否是对象
 */
export function isObject (obj) {
  return obj !== null && typeof obj === 'object'
}

mutation

用法
export default createStore({
  mutations: {
    increment(state, n) {
      state.count += n;
    },
  },
});
<template>
  <div>
    <!-- 使用increment -->
    <button @click="increment">增加计数</button>
  </div>
</template>

<script lang="ts">
import { computed, defineComponent } from "vue";
import { useStore } from "vuex";

export default defineComponent({
  name: "App",
  setup() {

    const store = useStore();

    let increment = () => {
      // 调用 store 的 increment 方法,传参5
      store.commit("increment", 5);
    }

    return {
      increment,
    };
  }
});
</script>
代码实现

思路:
将所有的mutations放置在_mutations对象容器中{'方法名': 函数实现 }, store.commit调用的时候直接从_mutations对象容器中获取到对应的函数并且执行

export class Store {
  
  constructor(options) {

    // 创建一个_mutation容器
    this._mutations = Object.create(null);
    foreachValue(options.mutations, (mutation, key) => {
      this._mutations[key] = (payload) => {
        // 将this重新指向为实例对象store,可以接收两个参数,state和payload
        mutation.call(this, this.state, payload);
      };
    });

  }

  // 提交mutation的函数
  commit = (methodName, payload) => {
    // 从_mutation对象容器中找对应的函数
    let fn = this._mutations[methodName];
    if (fn) {
      // 执行fn, 传payload
      fn(payload);
    }
  }

}

Action

用法
export default createStore({
  actions: {
    <!-- action和mutation的第一个参数有较大区别 -->
    incrementAsync ({ commit }, payload) {
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          commit('increment', payload)
          resolve()
        }, 1000)
      })
    }
  }
});
<template>
  <div>
    <!-- 调用异步计数 -->
    <button @click="incrementAsync">异步计数</button>
  </div>
</template>

<script lang="ts">
import { computed, defineComponent } from "vue";
import { useStore } from "vuex";

export default defineComponent({
  name: "App",
  setup() {
    const store = useStore();

    let incrementAsync = () => {
      <!-- dispatch 异步调用, 返回一个Promise对象 -->
      store.dispatch("incrementAsync", 10);
    };
    
    return {
      incrementAsync,
    };
  },
});
</script>

代码实现

思路:
所有的actions放置在_actions对象容器中{'方法名': 函数实现 }, store.dispatch调用的时候直接从_actions对象容器中获取到对应的函数并且执行

异步的操作需要用到Promise

export class Store {
  
  constructor(options) {

    this._actions = Object.create(null);
    // 遍历所有的actions函数 
    foreachValue(options.actions, (action, key) => {
      this._actions[key] = (payload) => {
        // 调用函数, 将一些上下文传递,因为 异步的操作 不仅是操作 state, 还可能需要操作commit,getters,dispatch等。
        let res = action.call(this, {
          dispatch: this.dispatch,
          commit: this.commit,
          getters: this.getters,
          state: this.state,
        }, payload);
        // 函数可能是异步也可能是同步, 如果同步则通过`Promise.resolve`将函数结果转为成功的Promise
        if (!isPromise(res)) {
          res = Promise.resolve(res);
        }
        return res;
      }
    })

  }

  dispatch = (methodName, payload) => {
    let fn = this._actions[methodName];
    if (fn) {
      let result = fn(payload);
      // 返回一个Promise对象,它的状态和结果由result决定
      return new Promise((resolve, reject) => {
        result.then(
          (res) => {
            resolve(res);
          },
          (error) => {
            reject(error);
          }
        );
      });
    }
  }

}

/**
 * 
 * @param {*} val 传入的参数
 * @returns 是否是Promise对象
 */
export function isPromise (val) {
  return val && typeof val.then === 'function'
}

版本一完成

至此,四部分功能都已经实现了,包括state, getters, mutaions, actions

全部代码如下:

import { reactive } from 'vue';

const storeKey = "store";

export function createStore(options) {
  return new Store(options);
}

export class Store {
  
  constructor(options) {

    this._state = reactive({data: options.state || Object.create(null)});
    
    this.getters = {};
    foreachValue(options.getters, (value, key) => {
      Object.defineProperty(this.getters, key, {
        get: () => value(this.state)
      })
    });

    this._mutations = Object.create(null);
    foreachValue(options.mutations, (mutation, key) => {
      this._mutations[key] = (payload) => {
        mutation.call(this, this.state, payload);
      };
    });

    this._actions = Object.create(null);
    foreachValue(options.actions, (action, key) => {
      this._actions[key] = (payload) => {
        let res = action.call(this, {
          dispatch: this.dispatch,
          commit: this.commit,
          getters: this.getters,
          state: this.state,
        }, payload);
        if (!isPromise(res)) {
          res = Promise.resolve(res);
        }
        return res;
      }
    })

  }

  commit = (methodName, payload) => {
    let fn = this._mutations[methodName];
    if (fn) {
      fn(payload);
    }
  }

  dispatch = (methodName, payload) => {
    let fn = this._actions[methodName];
    if (fn) {
      let result = fn(payload);
      return new Promise((resolve, reject) => {
        result.then(
          (res) => {
            resolve(res);
          },
          (error) => {
            reject(error);
          }
        );
      });
    }
  }

  get state() {
    return this._state.data;
  }

  install(app, ...options) {
    
    app.provide(storeKey, this);
    app.config.globalProperties.$store = this;

  }

}

export function useStore() {
  return inject(storeKey);
}

export function foreachValue(obj, fn) {
  if (isObject(obj)) {
    Object.keys(obj).forEach(key => fn(obj[key], key));
  }
}

export function isObject (obj) {
  return obj !== null && typeof obj === 'object'
}

export function isPromise (val) {
  return val && typeof val.then === 'function'
}

modules

我们举的例子有一个子模块aModule, 子模块aModule下有一个子模块bModule

export default createStore({
  state: {
    count: 1,
  },
  getters: {
    plusOne: ...
  },
  mutations: {
    increment: ...
  },
  actions: {
    incrementAsync: ...
  },
  modules: {
    aModule: {
      state: {
        count: 1,
      },
      getters: {
        plusOneA: ...
      },
      mutations: {
        incrementA: ...
      },
      actions: {
        incrementAsyncA: ...
      },
      modules: {
        bModule: {
          state: {
            count: 1,
          },
          getters: {
            plusOneB: ...
          },
          mutations: {
            incrementB: ...
          },
          actions: {
            incrementAsyncB: ...
          }
        },
      },
    },
  },
});
代码实现
  • 首先将options参数进行格式化,方便进行后续的数据管理:

改造成:

root = {
    _rawModule: rootModule({ state, geters, mutations, actions, modules}),
    state: rootModule.state,
    _childeren: {
        aModule: {
            _rawModule: aModule({ state, geters, mutations, actions, modules}),
            state: aModule.state,
            _childeren: {
                bModule: {
                    _rawModule: bModule({ state, geters, mutations, actions, modules}),
                    state: bModule.state,
                    _childeren: {}
                }
            }
        }
    }
}

代码如下:

<!-- ModuleCollection.js -->
export default class ModuleCollection {

  constructor(rawRootModule) {
    // 这个方法是深度递归遍历的方法,从根部进行开始
    this.register([], rawRootModule, false);
  }

  /**
   * 递归进行遍历组织数据
   * @param {*} path 模块的名称的深度数组
   * @param {*} rawModule 模块对应的原始moudles数据
   */
  register(path, rawModule) {
    
    // 先将数据封装成Module,此时的_children还未赋值
    const newModule = new Module(rawModule);

    if (path.length == 0) {
      // 此时说明是根模块, 给root赋值
      this.root = newModule; // 这个对象其实就是createStore传入的options得到的数据
    } else {
      // 此时说明是子模块, 此时需要把自己赋值给父模块的_children
      // 找到父模块 , path.slice(0, -1) 是排除最后一个元素的数组(eg.  ["aModule", "bModule"] 得到的是 ["aModule"]),就是父模块的路劲
      const parent = this.get(path.slice(0, -1));
      console.log(path);
      console.log(parent);

      // 把newModule设置为父module的_children (eg. path[path.length - 1] 就是 "bModule", 值为 模块值 )
      parent.addChild(path[path.length - 1], newModule);
    }

    // 如果有 rawModule 有 moudules 属性, 则递归调用,递归调用时将子模块赋值当前newModule的_children
    if (rawModule.modules) {
      foreachValue(rawModule.modules, (rawChildModule, key) => {
        // path:遍历aModule的时候为 ["aModule"], bModule的时候 ["aModule", "bModule"]
        this.register(path.concat(key), rawChildModule);
      })
    }
  }

  /**
   * 这个方法是一步步深入找到对应的module
   * @param {*} path 模块路径数组
   * @returns 根据模块路径数组找到对应的module数据
   */
  get(path) {
    return path.reduce((module, key) => {
      return module.getChild(key);
    }, this.root);
  }

}

<!-- Module.js -->
export default class Module {
  constructor(rawModule) {
    // 存储原始的数据
    this._rawModule = rawModule;

    // 获取state
    const rawState = rawModule.state;
    this.state = (typeof rawState === "function" ? rawState() : rawState) || {};

    // _children的属性
    this._children = Object.create(null);
  }

  // 设置_children属性
  addChild(key, module) {
    this._children[key] = module;
  }

  // 获取_children属性
  getChild(key) {
    return this._children[key];
  }
}

  • 把格式化的_module进行安装,包括对state,getter,mutations,actions进行处理

改造成:

store._modules.root.state = {
    count: 1,
    aModule: {
        count: 1,
        bModule: {
            count: 1
        }
    }
}

store._wrappedGetters = {
    plusOne: fun(), 
    plusOneA: fun(), 
    plusOneB: fun()
}

store._mutations = {
    increment: [fun()],
    incrementA: [fun()],
    incrementB: [fun()],
}

store._actions = {
    incrementAsync: [fun()],
    incrementAsyncA: [fun()],
    incrementAsyncB: [fun()],
}

代码如下:

<!-- Module。js -->
export default class Module {
  // 省略其他逻辑    
    
  // 获取对应的getter函数
  forEachGetter(fn) {
    if (this._rawModule.getters) {
      foreachValue(this._rawModule.getters, fn);
    }
  }

  // 获取对应的actions函数
  forEachAction(fn) {
    if (this._rawModule.actions) {
      foreachValue(this._rawModule.actions, fn);
    }
  }

  // 获取对应的mutations函数
  forEachMutation(fn) {
    if (this._rawModule.mutations) {
      foreachValue(this._rawModule.mutations, fn);
    }
  }
}
<!-- Store.js -->
export class Store {
  constructor(options) {

    // 格式化
    this._modules = new ModuleCollection(options);
    
    // 数据初始
    this._actions = Object.create(null);
    this._mutations = Object.create(null);
    this._wrappedGetters = Object.create(null);

    // 安装: 处理
    const state = this._modules.root.state;
    installModule(this, state, [], this._modules.root);

  }

}


/**
 * 组织数据包括 state,getters, mutations, actions 的整理
 * @param {*} store store 对象
 * @param {*} rootState 根state对象
 * @param {*} path 模块路径数组(eg. ["aModule", "bModule"])
 * @param {*} module 当前的模块Module数据
 */
export function installModule(store, rootState, path, module) {
  // 是否是根,(空数组是根)
  const isRoot = !path.length;

  // ******** state 处理逻辑:********
  // 如果不是根,就将其按照树状结构挂载到根rootState上
  if (!isRoot) {
    // 和 Module类似, 将所有的state组织成树状结构
    const parentState = getNestedState(rootState, path.slice(0, -1));
    const moduleName = path[path.length - 1];
    parentState[moduleName] = module.state;
  }

  // local是指当前 module 的局部数据
  const local = {
    dispatch: store.dispatch,
    commit: store.commit,
    getters: store.getters,
  };
  Object.defineProperties(local, {
    state: {
      get: () => getNestedState(store.state, path),
    },
  });
  // 局部数据保存到module上
  module.context = local;

  // ******** getters 处理逻辑:********
  module.forEachGetter((getter, key) => {
    registerGetter(store, key, getter, local);
  });

  // ******** actions 处理逻辑:********
  module.forEachAction((action, key) => {
    registerAction(store, key, action, local);
  });

  // ******** mutations 处理逻辑:********
  module.forEachMutation((mutation, key) => {
    registerMutation(store, key, mutation, local);
  });

  // 遍历子节点
  foreachValue(module._children, (childModule, key) => {
    installModule(store, rootState, path.concat(key), childModule);
  });
}

// 获取对应模块的 state
export function getNestedState(state, path) {
  return path.reduce((state, key) => state[key], state);
}

/**
 * 将函数添加到_mutations[type]数组中
 * @param {*} store store对象
 * @param {*} type mutation的key
 * @param {*} handler mutation函数
 * @param {*} local 局部module数据
 */
function registerMutation(store, type, handler, local) {
  // _mutations[type]对应的是一个数组,但是 每个函数操作的是 局部的state数据 
  const entry = store._mutations[type] || (store._mutations[type] = []);
  entry.push(function (payload) {
    handler.call(store, local.state, payload);
  });
}

/**
 *  将函数以属性的形式添加到_wrapperdGetters上
 * @param {*} store store对象
 * @param {*} type getter的key
 * @param {*} rawGetter 
 * @param {*} local 
 * @returns 
 */
function registerGetter(store, type, getter, local) {
  store._wrappedGetters[type] = (store) => {
    return getter(
      local.state, // local state
      local.getters, // local getters
      store.state, // root state
      store.getters // root getters
    );
  };
}

/**
 * 将函数添加到_actions[type]数组中
 * @param {*} store store对象
 * @param {*} type action的key
 * @param {*} handler action的函数
 * @param {*} local 局部数据对象
 */
function registerAction(store, type, handler, local) {
  const entry = store._actions[type] || (store._actions[type] = []);
  entry.push(function wrappedActionHandler(payload) {
    let res = handler.call(
      store,
      {
        dispatch: local.dispatch,
        commit: local.commit,
        getters: local.getters,
        state: local.state,
        rootGetters: store.getters,
        rootState: store.state,
      },
      payload
    );
    if (!isPromise(res)) {
      res = Promise.resolve(res);
    }
    return res;
  });
}

  • 设置响应式数据_state和处理_wrapperdGetters中的数据
export function resetStoreState(store, state) {

  // 响应式数据 
  store._state = reactive({
    data: state,
  });

  // _wrappedGetters 转换成 getters 属性
  store.getters = {};

  const wrappedGetters = store._wrappedGetters;
  foreachValue(wrappedGetters, (fn, key) => {
    Object.defineProperty(store.getters, key, {
      get: () => fn(store),
      enumerable: true, // for local getters
    });
  });

}
export class Store {
  constructor(options) {

    // 格式化
    this._modules = new ModuleCollection(options);
    
    // 数据初始
    this._actions = Object.create(null);
    this._mutations = Object.create(null);
    this._wrappedGetters = Object.create(null);

    // 安装: 处理
    const state = this._modules.root.state;
    installModule(this, state, [], this._modules.root);

    // state 和 getter 的 逻辑
    resetStoreState(this, state);

  }

}

  • 需要修改commitdispatch方法
  commit = (methodName, payload) => {
    let funArr = this._mutations[methodName];
    if (funArr) {
      // 每个函数都执行
      funArr.forEach((fn) => {
        fn(payload);
      })      
    }
  };

  dispatch = (methodName, payload) => {
    let funArr = this._actions[methodName];
    if (funArr) {
      // 所以异步任务都执行完
      const result =
      funArr.length > 1
        ? Promise.all(funArr.map((handler) => handler(payload)))
        : funArr[0](payload);

      return new Promise((resolve, reject) => {
        result.then(
          (res) => {
            resolve(res);
          },
          (error) => {
            reject(error);
          }
        );
      });
    }
  };

版本二完成

至此,四部分功能都已经实现了,包括state, getters, mutaions, actionsmodules版本。

<!-- Module.js -->
import { foreachValue } from "../vuex";

export default class Module {
  constructor(rawModule) {
    // 存储原始的数据
    this._rawModule = rawModule;

    // 获取state
    const rawState = rawModule.state;
    this.state = (typeof rawState === "function" ? rawState() : rawState) || {};

    // _children的属性
    this._children = Object.create(null);
  }

  // 设置_children属性
  addChild(key, module) {
    this._children[key] = module;
  }

  // 获取_children属性
  getChild(key) {
    return this._children[key];
  }

  // 获取对应的getter函数
  forEachGetter(fn) {
    if (this._rawModule.getters) {
      foreachValue(this._rawModule.getters, fn);
    }
  }

  // 获取对应的actions函数
  forEachAction(fn) {
    if (this._rawModule.actions) {
      foreachValue(this._rawModule.actions, fn);
    }
  }

  // 获取对应的mutations函数
  forEachMutation(fn) {
    if (this._rawModule.mutations) {
      foreachValue(this._rawModule.mutations, fn);
    }
  }
}

<!-- ModuleCollection.js -->
import Module from "./module"
import { foreachValue } from "../vuex"

export default class ModuleCollection {

  constructor(rawRootModule) {
    // 这个方法是深度递归遍历的方法,从根部进行开始
    this.register([], rawRootModule, false);
  }

  /**
   * 递归进行遍历组织数据
   * @param {*} path 模块的名称的深度数组
   * @param {*} rawModule 模块对应的原始moudles数据
   */
  register(path, rawModule) {
    
    // 先将数据封装成Module,此时的_children还未赋值
    const newModule = new Module(rawModule);

    if (path.length == 0) {
      // 此时说明是根模块, 给root赋值
      this.root = newModule; // 这个对象其实就是createStore传入的options得到的数据
    } else {
      // 此时说明是子模块, 此时需要把自己赋值给父模块的_children
      // 找到父模块 , path.slice(0, -1) 是排除最后一个元素的数组(eg.  ["aModule", "bModule"] 得到的是 ["aModule"]),就是父模块的路劲
      const parent = this.get(path.slice(0, -1));
      console.log(path);
      console.log(parent);

      // 把newModule设置为父module的_children (eg. path[path.length - 1] 就是 "bModule", 值为 模块值 )
      parent.addChild(path[path.length - 1], newModule);
    }

    // 如果有 rawModule 有 moudules 属性, 则递归调用,递归调用时将子模块赋值当前newModule的_children
    if (rawModule.modules) {
      foreachValue(rawModule.modules, (rawChildModule, key) => {
        // path:遍历aModule的时候为 ["aModule"], bModule的时候 ["aModule", "bModule"]
        this.register(path.concat(key), rawChildModule);
      })
    }
  }

  /**
   * 这个方法是一步步深入找到对应的module
   * @param {*} path 模块路径数组
   * @returns 根据模块路径数组找到对应的module数据
   */
  get(path) {
    return path.reduce((module, key) => {
      return module.getChild(key);
    }, this.root);
  }

}
import { reactive } from "vue";
import ModuleCollection from "../vuex-mine/module/module-collection";

const storeKey = "store";

export function createStore(options) {
  return new Store(options);
}

export class Store {
  constructor(options) {

    // 格式化
    this._modules = new ModuleCollection(options);
    
    // 数据初始
    this._actions = Object.create(null);
    this._mutations = Object.create(null);
    this._wrappedGetters = Object.create(null);

    // 安装: 处理
    const state = this._modules.root.state;
    installModule(this, state, [], this._modules.root);

    // state 和 getter 的 逻辑
    resetStoreState(this, state);

  }

  commit = (methodName, payload) => {
    let funArr = this._mutations[methodName];
    if (funArr) {
      // 每个函数都执行
      funArr.forEach((fn) => {
        fn(payload);
      })      
    }
  };

  dispatch = (methodName, payload) => {
    let funArr = this._actions[methodName];
    if (funArr) {
      // 所以异步任务都执行完
      const result =
      funArr.length > 1
        ? Promise.all(funArr.map((handler) => handler(payload)))
        : funArr[0](payload);

      return new Promise((resolve, reject) => {
        result.then(
          (res) => {
            resolve(res);
          },
          (error) => {
            reject(error);
          }
        );
      });
    }
  };

  get state() {
    return this._state.data;
  }

  install(app, ...options) {
    app.provide(storeKey, this);
    app.config.globalProperties.$store = this;
  }
}

export function useStore() {
  return inject(storeKey);
}

/**
 * 组织数据包括 state,getters, mutations, actions 的整理
 * @param {*} store store 对象
 * @param {*} rootState 根state对象
 * @param {*} path 模块路径数组(eg. ["aModule", "bModule"])
 * @param {*} module 当前的模块Module数据
 */
export function installModule(store, rootState, path, module) {
  // 是否是根,(空数组是根)
  const isRoot = !path.length;

  // ******** state 处理逻辑:********
  // 如果不是根,就将其按照树状结构挂载到根rootState上
  if (!isRoot) {
    // 和 Module类似, 将所有的state组织成树状结构
    const parentState = getNestedState(rootState, path.slice(0, -1));
    const moduleName = path[path.length - 1];
    parentState[moduleName] = module.state;
  }

  // local是指当前 module 的局部数据
  const local = {
    dispatch: store.dispatch,
    commit: store.commit,
    getters: store.getters,
  };
  Object.defineProperties(local, {
    state: {
      get: () => getNestedState(store.state, path),
    },
  });
  // 局部数据保存到module上
  module.context = local;

  // ******** getters 处理逻辑:********
  module.forEachGetter((getter, key) => {
    registerGetter(store, key, getter, local);
  });

  // ******** actions 处理逻辑:********
  module.forEachAction((action, key) => {
    registerAction(store, key, action, local);
  });

  // ******** mutations 处理逻辑:********
  module.forEachMutation((mutation, key) => {
    registerMutation(store, key, mutation, local);
  });

  // 遍历子节点
  foreachValue(module._children, (childModule, key) => {
    installModule(store, rootState, path.concat(key), childModule);
  });
}

// 获取对应模块的 state
export function getNestedState(state, path) {
  return path.reduce((state, key) => state[key], state);
}

/**
 * 将函数添加到_mutations[type]数组中
 * @param {*} store store对象
 * @param {*} type mutation的key
 * @param {*} handler mutation函数
 * @param {*} local 局部module数据
 */
function registerMutation(store, type, handler, local) {
  // _mutations[type]对应的是一个数组,但是 每个函数操作的是 局部的state数据 
  const entry = store._mutations[type] || (store._mutations[type] = []);
  entry.push(function (payload) {
    handler.call(store, local.state, payload);
  });
}

/**
 *  将函数以属性的形式添加到_wrapperdGetters上
 * @param {*} store store对象
 * @param {*} type getter的key
 * @param {*} rawGetter 
 * @param {*} local 
 * @returns 
 */
function registerGetter(store, type, getter, local) {
  store._wrappedGetters[type] = (store) => {
    return getter(
      local.state, // local state
      local.getters, // local getters
      store.state, // root state
      store.getters // root getters
    );
  };
}

/**
 * 将函数添加到_actions[type]数组中
 * @param {*} store store对象
 * @param {*} type action的key
 * @param {*} handler action的函数
 * @param {*} local 局部数据对象
 */
function registerAction(store, type, handler, local) {
  const entry = store._actions[type] || (store._actions[type] = []);
  entry.push(function wrappedActionHandler(payload) {
    let res = handler.call(
      store,
      {
        dispatch: local.dispatch,
        commit: local.commit,
        getters: local.getters,
        state: local.state,
        rootGetters: store.getters,
        rootState: store.state,
      },
      payload
    );
    if (!isPromise(res)) {
      res = Promise.resolve(res);
    }
    return res;
  });
}

export function resetStoreState(store, state) {

  // 响应式数据 
  store._state = reactive({
    data: state,
  });

  // _wrappedGetters 转换成 getters 属性
  store.getters = {};

  const wrappedGetters = store._wrappedGetters;
  foreachValue(wrappedGetters, (fn, key) => {
    Object.defineProperty(store.getters, key, {
      get: () => fn(store),
      enumerable: true, // for local getters
    });
  });

}

/**
 * 遍历对象属性执行函数
 * @param {*} obj 需要遍历的对象
 * @param {*} fn 对象的每个key值, 执行函数,传参为 value 和 key
 */
export function foreachValue(obj, fn) {
  if (isObject(obj)) {
    Object.keys(obj).forEach((key) => fn(obj[key], key));
  }
}

/**
 *
 * @param {*} obj 传入参数
 * @returns 是否是对象
 */
export function isObject(obj) {
  return obj !== null && typeof obj === "object";
}

/**
 *
 * @param {*} val 传入的参数
 * @returns 是否是Promise对象
 */
export function isPromise(val) {
  return val && typeof val.then === "function";
}

本文主要是分析代码实现,源码请参阅 Vuex GitHub地址

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 198,082评论 5 464
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,231评论 2 375
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 145,047评论 0 327
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,977评论 1 268
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,893评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,014评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,976评论 3 388
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,605评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,888评论 1 293
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,906评论 2 314
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,732评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,513评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,980评论 3 301
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,132评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,447评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,027评论 2 343
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,232评论 2 339

推荐阅读更多精彩内容