Skip to content

plugin

javascript
class Store {
  constructor (options = {}) {
    this._actionSubscribers = []
    this._subscribers = []

    // 插件就是一个函数,构造器中会遍历配置项中的 plugins 数组并将里面的每个函数执行的
    // 同时传入 store,插件只会执行这一次,这一次执行会完成插件初始化和回调收集。
    // store._subscribers 中保存着所有插件的回调,插件内部通过 store.subscribe 
    // 收集回调,接收 fn 和 options 两个参数,如果 store._subscribers.indexOf(fn)
    // >= 0,那么根据 options.prepend 是否为真决定添加到回调数组的最前面还是最后面。
    // 添加完后返回一个函数用于删除回调,这个函数调用时会获取该回调在数组中的位置,
    // 并通过 splice 在数组 store._subscribers 删除该回调。
    // 在 commit 方法中最后会将 store._subscribers 中的每个 fn 执行,因此插件的回调
    // 只有在 mutation 执行完后才会执行,fn 执行时会传入 mutation 和 state。
    // 当然回调也可以保存在 store._actionSubscribers 中,这里保存的每一项是对象
    // 如果你传的是 fn,会包装成 { before: fn },意味着 fn 会在 在 action 执行前执行
    // 你可以手动配置成 { after: fn } 或 { error: fn },意味着 fn 会在 action 执行
    // 完后执行或出错时执行
    // apply plugins
    plugins.forEach(plugin => plugin(this))
  }

  commit (type, payload) {
    const entry = this._mutations[type]
    if (!entry) {
      return
    }
    entry.forEach(function commitIterator (handler) {
      handler(payload)
    })

    this._subscribers
      .slice() // shallow copy to prevent iterator invalidation if subscriber synchronously calls unsubscribe
      .forEach(sub => sub(mutation, this.state))
  }

  dispatch (type, payload) {
    const entry = this._actions[type]
    if (!entry) {
      return
    }
    this._actionSubscribers
      .slice() // shallow copy to prevent iterator invalidation if subscriber synchronously calls unsubscribe
      .filter(sub => sub.before)
      .forEach(sub => sub.before(action, this.state))

    const result = entry.length > 1
      ? Promise.all(entry.map(handler => handler(payload)))
      : entry[0](payload)

    return new Promise((resolve, reject) => {
      result.then(res => {
        this._actionSubscribers
          .filter(sub => sub.after)
          .forEach(sub => sub.after(action, this.state))
        resolve(res)
      }, error => {
        this._actionSubscribers
          .filter(sub => sub.error)
          .forEach(sub => sub.error(action, this.state, error))
        reject(error)
      })
    })
  }
}