简易版VUEX

563 阅读1分钟

1. 在写VUEX之前先了解VUE中组件的注册

  1. 在调用vue.use()会调用组件的install方法
  2. Vue.mixin()会在全局的vue实例中都会获取到
    先了解VUEX的基本使用方法 vuex.vuejs.org/zh/

2. 文件结构

vuex
----index.js
----mixin.js
----store.js
----util.js

3. 代码实现

index.js

import { Store, install } from "./store";

export default {
  Store,
  install
};

store.js

import applyMixin from "./mixin";
import { ModuleCollecton, installModule } from "./util";
export let Vue;

class Store {
  constructor(options) {
    const state = options.state;
    this._vm = new Vue({
      data: {
        state
      }
    });

    // 处理getter
    this.getters = {};
    // forEachValue(options.getters || {}, (fn, key) => {
    //   Object.defineProperty(this.getters, key, {
    //     get: () => { console.log('我变了'); return fn(this.state);}
    //   });
    // });

    // 处理mutation
    this.mutations = {};
    // forEachValue(options.mutations || {}, (fn, key) => {
    //   this.mutations[key] = (payload) => {
    //     fn(this.state, payload)
    //   }
    // })

    // 处理actions
    // 最后会做一个监控 看看是不是异步方法都在action更新 不是在mutation执行的
    this.actions = {};
    // forEachValue(options.actions || {}, (fn, key) => {
    //   this.actions[key] = (payload) => {
    //     fn(this, payload)
    //   }
    // })
    // ------------------------------------------------------------------

    // 想成为这种格式
    // let root = {
    //   _row: rootModule,
    //   state: rootModule.state,
    //   _children: {
    //     a: {
    //       _row: aModule,
    //       state: aModule.state,
    //       _children: {},
    //     },
    //     b: {
    //       _row: bModule,
    //       state: bModule.state,
    //       _children: {
    //         c: {
    //           _row: cModule,
    //           state: cModule.state,
    //           _children: {}
    //         }
    //       }
    //     }
    //   }
    // }

    // 格式化
    this.modules = new ModuleCollecton(options)
    console.log(this.modules);

    // 递归的安装模块 比如action mutation getters
    // 对该vue实例state 进行安装
    // 把上面的变为
    // 数据拍平
    // this.getters = {
    //   a: (stateA) => {},
    //   b: (stateB) => {},
    //   c: (stateC) => {},
    // };
    // 数据拍平
    // this.mutations = {
    //   a: [(stateA) => {}, (stateAa) => {}],
    //   b: [(stateB) => {}, (stateBa) => {}],
    //   c: [(stateC) => {}, (stateCc) => {}],
    // };
    // 形成属性结构 注意这里必须使用$set方式 否则无法响应数据
    // state = {
    //   a: {
    //     age: '123',
    //     b: {
    //       age: '345'
    //     }
    //   }
    // }
    installModule(this, this.state, [], this.modules.root)
  }
  // 处理state
  get state() {
    return this._vm.state;
  }
  // 处理commit
  commit = (mutationName, payload) => { // this指向
    this.mutations[mutationName].forEach(fn => {
      fn(payload)
    });
  }
  // 处理dispatch
  dispatch = (actionName, payload) => {
    this.actions[actionName].forEach(fn => {
      fn(payload)
    })
  }
  // 动态注册
  registerModule = (moduleName, module) => {
    if (!Array.isArray(moduleName)) {
      moduleName = [moduleName]
    }
    this.modules.register(moduleName, module) // 只进行了数据格式化
    console.log(this.modules);
    installModule(this, this.state, moduleName, this.modules.root)
  }
}

// vue的构造函数
export function install(_vue) {
  Vue = _vue;
  applyMixin(Vue);
}

export {Store}

util.js

import {Vue} from './store'
export const forEachValue = (obj, callback) => {
  Object.keys(obj).forEach(key => callback(obj[key], key));
};

export class ModuleCollecton {
  constructor(options) {
    // 深度遍历 进行模块集合
    this.register([], options)
  }
  register(path, rootModule) {
    let rowModule = {
      _row: rootModule,
      state: rootModule.state,
      _children: {}
    }
    if (!this.root) {
      this.root = rowModule
    } else {
      let parentModule = path.slice(0, -1).reduce((pre, cur) => {
        return pre._children[cur]
      }, this.root)

      parentModule._children[path[path.length - 1]] = rowModule
    }
    if (rootModule.modules) {
      forEachValue(rootModule.modules, (moduleVal, moduleName) => {
        this.register(path.concat(moduleName), moduleVal)
      })
    }
  }
}

export function installModule(store, rootState, path, rowModule) { // _row, _children, state
  let getters = rowModule._row.getters
  if(getters) {
    // installmodule第二次会重复 但是这个方法不可以
    forEachValue(getters, (val, getterName) => {
      if (!getters[getterName]) {
        Object.defineProperty(store.getters, getterName, {
          get: () => {
            return val(rowModule.state) // 模块中的状态
          }
        })
      }
    })
  }

  let mutations = rowModule._row.mutations
  if(mutations) {
    forEachValue(mutations, (val, mutationName) => {
      let arr = store.mutations[mutationName] || (store.mutations[mutationName] = [])
      arr.push((payload) => {
        val(rowModule.state, payload)
      })
    })
  }

  let actions = rowModule._row.actions
  if(actions) {
    forEachValue(actions, (val, actionName) => {
      let arr = store.actions[actionName] || (store.actions[actionName] = [])
      arr.push((payload) => {
        val(store, payload)
      })
    })
  }

  let state = rowModule._row.state
  if (state) {
    if(path.length > 0) { // 有子模块
      // Vue的响应式原理 不能增加不存在的属性
      let parentState = path.slice(0, -1).reduce((pre, cur) => {
        return pre[cur]
      }, rootState)
      
      Vue.set(parentState, path[path.length - 1], rowModule.state)
    }
  }

  forEachValue(rowModule._children, (module, moduleName) => {
    installModule(store, rootState, path.concat(moduleName), module)
  })
}

mixin.js

export default function applyMixin(Vue) {
  console.log(Vue);
  Vue.mixin({
    beforeCreate: vuexInit
  });
}

// 这个生命周期会提前执行 优先于子组件的生命周期
function vuexInit() {
  const options = this.$options;
  // 根实例
  if (options.store) {
    this.$store = options.store;
  } else if (options.parent && options.parent.$store) {
    this.$store = options.parent.$store;
  }
}