vuex实现原理(-)

305 阅读1分钟

app.vue

<template>
  <div id="app">
    <!-- <div id="nav"> -->
      ceshi
     {{this.$store.state.count}}
    {{this.$store.getters.newCount}}
    {{this.$store.state.a.b.count}}
    <button @click="change">add</button>
    <!-- </div> -->
    <!-- <router-view/> -->
  </div>
</template>
<script>
export default {
  name: 'app',
  data() {
    return {
    };
  },
  mounted() {
    console.log(this.$store.state, '+++++++');
  },
  methods: {
    change() {
      this.$store.dispatch('change');
    },
  },
};
</script>
<style lang="stylus">

</style>


sotre --》 index.js

import Vue from 'vue';
import Vuex from '@/vuex.js';

Vue.use(Vuex); // 调用库里面的install方法

export default new Vuex.Store({
  // 状态分层
  modules: { // 递归
    a: {
      state: {
        count: 200,
      },
      mutations: {
        // eslint-disable-next-line no-unused-vars
        change(state) {
          console.log('xxxx');
          // 发布订阅,所有方法都放在一个数组里面
        },
      },
      modules: {
        b: {
          state: {
            count: 3000,
          },
        },
      },
    },
  },
  state: {
    // 状态
    count: 100,
  },
  getters: {
    // 状态的计算属性
    newCount(state) { // 200
      return state.count + 100;
    },
  },
  mutations: {
    // 同步更新状态
    change(state) {
      state.count += 10;
      // console.log(this.state);
      console.log(this.state.count += 10);
    },
  },
  actions: {
    change({ commit }) {
      setTimeout(() => {
        commit('change');
      }, 1000);
    },
  },
});

vuex.js

/* eslint-disable no-use-before-define */
/* eslint-disable no-underscore-dangle */
let Vue;
class ModuleCollection {
  constructor(options) {
    // vuex []
    this.register([], options);
  }

  register(path, rawModule) {
    // path 是个空数组 rawModule即使个对象
    const newModule = {
      _raw: rawModule, // 对象 当前 有state,getters 那个对象
      _children: {}, // 表示 他包含的模块
      state: rawModule.state, // 自己的模块状态
    };
    if (path.length === 0) {
      this.root = newModule; // 根
    } else {
      // eslint-disable-next-line no-underscore-dangle
      const parent = path.slice(0, -1).reduce((root, current) => root._children[current],
        this.root);
      // eslint-disable-next-line no-underscore-dangle
      parent._children[path[path.length - 1]] = newModule;
    }
    if (rawModule.modules) {
      // eslint-disable-next-line no-use-before-define
      forEach(rawModule.modules, (childName, module) => {
        // [a]
        this.register(path.concat(childName), module);
      });
    }
  }
}
function installModule(store, rootState, path, rootModule) {
  // store 状态  空数组 更模块
  if (path.length > 0) {
    // 第日次获取到的就是a对应的对象
    // eslint-disable-next-line max-len
    const parent = path.slice(0, -1).reduce((root, current) => root[current], rootState); // row, children,state
    // {count:1000,a:{}}
    Vue.set(parent, path[path.length - 1], rootModule.state);
  }
  // eslint-disable-next-line no-undef
  // console.log(modules, '-------------');
  if (rootModule._raw.getters) {
    // eslint-disable-next-line no-underscore-dangle
    forEach(rootModule._raw.getters, (getterName, getterFn) => {
      Object.defineProperty(store.getters, getterName, {
        get: () => getterFn(rootModule.state),
      });
    });
  }
  if (rootModule._raw.actions) {
    // eslint-disable-next-line no-underscore-dangle
    forEach(rootModule._raw.actions, (actionName, actionFn) => {
      // eslint-disable-next-line no-param-reassign
      const entry = store.actions[actionName] || (store.actions[actionName] = []);
      entry.push(() => {
        actionFn.call(store, store);
      });
    });
  }
  // eslint-disable-next-line no-underscore-dangle
  if (rootModule._raw.mutations) {
    // eslint-disable-next-line no-underscore-dangle
    // eslint-disable-next-line no-use-before-define
    forEach(rootModule._raw.mutations, (mutationName, mutationFn) => {
      // eslint-disable-next-line no-param-reassign
      const entry = store.mutations[mutationName] || (store.mutations[mutationName] = []);
      entry.push(() => {
        mutationFn.call(store, rootModule.state);
      });
    });
  }

  // 判断儿子
  forEach(rootModule._children, (childName, module) => {
    installModule(store, rootState, path.concat(childName), module);
  });
}
class Store {
  // state getters mutations actions
  constructor(options) {
    const { state } = options;
    this.getters = {};
    this.mutations = {};
    this.actions = {};
    // console.log(this.s);
    // 什么样的属性可以实现双向 有get和set new Vue({data:{}})

    // vuex核心就是借用了vue的实例 因为vue的实例数据变化 会刷新视图
    // eslint-disable-next-line no-underscore-dangle
    this._vm = new Vue({
      data: {
        state,
      },
    });

    // 把模块之间的关系进行整理
    // root._children => a._children =>b
    this.modules = new ModuleCollection(options);
    // console.log(this.modules);
    // 无论子模块还是孙子 所有的mutation都是根上的
    console.log(this, '----');

    // this是store的实例 [] path this.modules.root当前的根模块
    installModule(this, state, [], this.modules.root); // _row _children

    // 只对根进行处理,还需要对孙子曾孙进行处理
    // if (options.getters) {
    //   const { getters } = options; // {newCount:fn}
    //   // eslint-disable-next-line no-use-before-define
    //   forEach(getters, (getterName, getterFn) => {
    //     Object.defineProperty(this.getters, getterName, {
    //       get: () => getterFn(state),
    //     });
    //   });
    // }
    // // mutation
    // const { mutations } = options;
    // // eslint-disable-next-line no-use-before-define
    // forEach(mutations, (mutationName, mutationFn) => {
    //   // this.mutations.change = () => {change(state)}
    //   this.mutations[mutationName] = () => {
    //     mutationFn.call(this, state);
    //   };
    // });
    // // actions
    // const { actions } = options;
    // // eslint-disable-next-line no-use-before-define
    // forEach(actions, (actionName, actionFn) => {
    //   this.actions[actionName] = () => {
    //     actionFn.call(this, this);
    //   };
    // });

    // eslint-disable-next-line comma-spacing
    const { commit, dispatch } = this; // 拷贝
    this.commit = (type) => {
      commit.call(this, type); // 绑定this指向
    };
    this.dispatch = (type) => {
      dispatch.call(this, type);
    };
  }

  get state() {
    // 类的访问器 Object.defineProperty里面的get同一个意思
    // eslint-disable-next-line no-underscore-dangle
    return this._vm.state;
  }

  commit(type) {
    this.mutations[type].forEach(fn => fn());
  }

  dispatch(type) {
    this.actions[type].forEach(fn => fn());// 绑定this,否则undefiend
  }
}
function forEach(obj, callback) {
  Object.keys(obj).forEach(item => callback(item, obj[item]));
}
const install = (_Vue) => {
  // console.log(_Vue, 'install');
  Vue = _Vue; // 保留vue的构造函数
  Vue.mixin({
    // 给每个组件混合一些东西(方法)
    beforeCreate() {
      // console.log('before create');
      // main.vue  App.vue 所以会走两次,因为所有组件创建之前都会加载beforeCreate
    // 我需要把根组件中 store实例 给每个组件都增加一个$store属性
    // 1. 判断是否是根组件
      if (this.$options && this.$options.store) {
        // console.log(this.$options.store);
        this.$store = this.$options.store;
      } else {
        // 子组件  深度优先 父组件=》子=》孙子
        // console.log(this.$options.name);
        this.$store = this.$parent && this.$parent.$store;
      }
    },
  });
};
export default {
  Store,
  install,
};