手写JavaScript简易全局状态管理库

316 阅读5分钟

前言

在上一篇文章中我们实现了手写JavaScript事件总线,这次我们准备结合之前我们所实现的事件总线来实现全局状态管理的功能,上篇文章链接:juejin.cn/post/718837…

需求分析

如果有使用过 Vuex、Pinia 的朋友都应该知道在使用他们的时候一般要传一个对象,这个对象里面至少会有两个属性,分别是 state、actions,这两个参数就不过多解释。我们只需要了解我们所实现的功能方法,大概包括如下几点:

  • onState(stateKey, eventCallback) 监听 state 的单个数据的改变
  • onStates(stateKeys, eventCallback) 监听 state 中多个数据的改变
  • offState(stateKey, eventCallback) 移除监听 state 单个数据的事件函数
  • offStates(stateKeys, eventCallback) 移除多个监听 state 数据的事件函数
  • setState(stateKey, newValue) 设置 state 数据
  • dispatch(actionKey, ...args) 执行 actions 中的 方法

编写代码

编写结构

// 上篇文章所实现的事件总线
const AriesEventBus = require("./event-bus");

class AriesEventStore {
    
    // 初始化操作
    constructor(options) {}
    
    // 给 state 中的每个属性添加属性描述符
    _observe() {}
    
    // 监听 state 中单个数据的改变
    onState(stateKey, eventCallback) {}
    
    // 监听 state 中多个数据的改变
    onStates(stateKeys, eventCallback) {}
    
    // 移除 state 中单个数据的对应的监听函数
    offState(stateKey, eventCallback) {}
    
    // 移除 state 中多个数据的对应的监听函数
    offStates(stateKeys, eventCallback) {}
    
    // 设置 state 中的数据 
    setState(stateKey, newValue) {}
    
    // 执行 actions 中对应的方法
    dispatch(actionKey, ...args) {}
}

实现初始化操作和 _observe() 方法

// 初始化函数
  constructor(options) {
    // 判断传入的 state 和 actions 是否是对象类型
    if (typeof options.state !== "object") {
      throw new TypeError("state 必须为 object 类型")
    }
    if (typeof options.actions !== "object") {
      throw new TypeError("actions 必须为 object 类型")
    }

    // 遍历 actions,判断 actions 中的属性值是否为方法
    const actionsValues = Object.values(options.actions)
    for (const action of actionsValues) {
      if (typeof action !== "function") {
        throw new TypeError("actions 中的每个属性值必须为 function 类型")
      }
    }

    this.actions = options.actions
    this.state = options.state

    // event:监听或移除单个
    this.event = new AriesEventBus()
    // eventV2:监听或移除多个
    this.eventV2 = new AriesEventBus()

    this._observe()

  }
  
  // 给 state 中的每个属性添加属性描述符
  _observe() {
    let _this = this
    Object.keys(this.state).forEach(key => {
      let _value = this.state[key]
      Object.defineProperty(this.state, key, {
        get() {
          return _value
        },
        set(newValue) {
          if (newValue === _value) return
          _value = newValue

          /**
           * event和eventV2其实就是实例化后的 eventBus 
           * 在 内部的 emit() 方法中如果 key 不存在于 eventBUs上
           * 则不会执行
           */
          // 在修改state的时候触发单个 key 的监听操作
          _this.event.emit(key, newValue)
          // 在修改state的时候触发多个 key 的监听操作
          _this.eventV2.emit(key, {[key]: newValue})
        }
      })
    })
  }

实现 onState() 和 onStates()

// 监听 state 中单个数据的改变
  onState(stateKey, eventCallback) {
    if (Object.keys(this.state).indexOf(stateKey) === -1) {
      throw Error("state 中不存在这个key:" + stateKey)
    }

    this.event.on(stateKey, eventCallback, this.state)

    eventCallback.apply(this.state, [this.state[stateKey]])
  }
  
  // 监听 state 中多个数据的改变
  onStates(stateKeys, eventCallback) {
    if (!Array.isArray(stateKeys)) {
      throw new TypeError("stateKeys必须为 Array 类型")
    }

    const keys = Object.keys(this.state)
    const value = {}
    for (const key of stateKeys) {
      if (keys.indexOf(key) === -1) {
        throw Error("state 中不存在这个 key:" + key)
      }
      this.eventV2.on(key, eventCallback)
      value[key] = this.state[key]
    }

    eventCallback.apply(this.state, [value])
    
  }

实现 offState() 和 offStates()

// 移除 state 中单个数据的对应的监听函数
  offState(stateKey, eventCallback) {
    if (Object.keys(this.state).indexOf(stateKey) === -1) {
      throw Error("state 中不存在这个 key:" + key)
    }

    this.event.off(stateKey, eventCallback)
  }
  
  // 移除 state 中多个数据的对应的监听函数
  offStates(stateKeys, eventCallback) {
    if (!Array.isArray(stateKeys)) {
      throw new TypeError("stateKeys必须为 Array 类型")
    }
    const keys = Object.keys(this.state)
    for (const key of stateKeys) {
      if (keys.indexOf(key) === -1) {
        throw Error("state 中不存在这个 key:" + key)
      }
      this.eventV2.off(key, eventCallback)
    }
  }

实现 setState() 和 dispatch()

// 设置 state 中的数据 
  setState(stateKey, newValue) {
    if (Object.keys(this.state).indexOf(stateKey) === -1) {
      throw Error("state 中不存在这个key:" + stateKey)
    }

    this.state[stateKey] = newValue
  }
  
  // 执行 actions 中对应的方法
  dispatch(actionKey, ...args) {
    if (Object.keys(this.actions).indexOf(actionKey) === -1) {
      throw Error("actions 中没有这个属性名:" + actionKey)
    }
    const action = this.actions[actionKey]
    action.apply(this, [this.state, args])
  }

完整代码

const AriesEventBus = require("./event-bus");

class AriesEventStore {
    
  // 初始化函数
  constructor(options) {
    // 判断传入的 state 和 actions 是否是对象类型
    if (typeof options.state !== "object") {
      throw new TypeError("state 必须为 object 类型")
    }
    if (typeof options.actions !== "object") {
      throw new TypeError("actions 必须为 object 类型")
    }

    // 遍历 actions,判断 actions 中的属性值是否为方法
    const actionsValues = Object.values(options.actions)
    for (const action of actionsValues) {
      if (typeof action !== "function") {
        throw new TypeError("actions 中的每个属性值必须为 function 类型")
      }
    }

    this.actions = options.actions
    this.state = options.state

    // event:监听或移除单个
    this.event = new AriesEventBus()
    // eventV2:监听或移除多个
    this.eventV2 = new AriesEventBus()

    this._observe()

  }
  
  // 给 state 中的每个属性添加属性描述符
  _observe() {
    let _this = this
    Object.keys(this.state).forEach(key => {
      let _value = this.state[key]
      Object.defineProperty(this.state, key, {
        get() {
          return _value
        },
        set(newValue) {
          if (newValue === _value) return
          _value = newValue

          /**
           * event和eventV2其实就是实例化后的 eventBus 
           * 在 内部的 emit() 方法中如果 key 不存在于 eventBUs上
           * 则不会执行
           */
          // 在修改state的时候触发单个 key 的监听操作
          _this.event.emit(key, newValue)
          // 在修改state的时候触发多个 key 的监听操作
          _this.eventV2.emit(key, {[key]: newValue})
        }
      })
    })
  }
  
  // 监听 state 中单个数据的改变
  onState(stateKey, eventCallback) {
    if (Object.keys(this.state).indexOf(stateKey) === -1) {
      throw Error("state 中不存在这个key:" + stateKey)
    }

    this.event.on(stateKey, eventCallback, this.state)

    eventCallback.apply(this.state, [this.state[stateKey]])
  }
  
  // 监听 state 中多个数据的改变
  onStates(stateKeys, eventCallback) {
    if (!Array.isArray(stateKeys)) {
      throw new TypeError("stateKeys必须为 Array 类型")
    }

    const keys = Object.keys(this.state)
    const value = {}
    for (const key of stateKeys) {
      if (keys.indexOf(key) === -1) {
        throw Error("state 中不存在这个 key:" + key)
      }
      this.eventV2.on(key, eventCallback)
      value[key] = this.state[key]
    }

    eventCallback.apply(this.state, [value])
    
  }
  
  // 移除 state 中单个数据的对应的监听函数
  offState(stateKey, eventCallback) {
    if (Object.keys(this.state).indexOf(stateKey) === -1) {
      throw Error("state 中不存在这个 key:" + key)
    }

    this.event.off(stateKey, eventCallback)
  }
  
  // 移除 state 中多个数据的对应的监听函数
  offStates(stateKeys, eventCallback) {
    if (!Array.isArray(stateKeys)) {
      throw new TypeError("stateKeys必须为 Array 类型")
    }
    const keys = Object.keys(this.state)
    for (const key of stateKeys) {
      if (keys.indexOf(key) === -1) {
        throw Error("state 中不存在这个 key:" + key)
      }
      this.eventV2.off(key, eventCallback)
    }
  }
  
  // 设置 state 中的数据 
  setState(stateKey, newValue) {
    if (Object.keys(this.state).indexOf(stateKey) === -1) {
      throw Error("state 中不存在这个key:" + stateKey)
    }

    this.state[stateKey] = newValue
  }
  
  // 执行 actions 中对应的方法
  dispatch(actionKey, ...args) {
    if (Object.keys(this.actions).indexOf(actionKey) === -1) {
      throw Error("actions 中没有这个属性名:" + actionKey)
    }
    const action = this.actions[actionKey]
    action.apply(this, [this.state, args])
  }
}

测试

const eventStore = new AriesEventStore({
  state: {
    name: "coder",
    age: 18
  },
  actions: {
    test(ctx, payload) {
      console.log(ctx);
      console.log(payload);
    }
  }
})

function nameCallback(...args) {
  console.log(args);
}
function manyCallback(...args) {
  console.log(args);
}
eventStore.onState('name', nameCallback)
eventStore.onStates(['name', 'age'], manyCallback)

setTimeout(() => {
  eventStore.setState('name', "zjl")
}, 1000);

setTimeout(() => {
  eventStore.offState('name', nameCallback)
}, 2000)

setTimeout(() => {
  eventStore.setState('name', "涂发亮")
  eventStore.setState('age', 30)
}, 3000);

setTimeout(() => {
  eventStore.dispatch('test', "I", "want", "you")
}, 4000);

image.png