「源码解析」深入学习redux源码

1,160 阅读6分钟

前言

众所周知,redux是react里面难度比较高,也比较重要的的一个知识点,这也是面试的高频面试题之一。那么不管为了自我的一个提升,还是为了面试准备,redux源码的学习都是必不可少。另外,源码的学习从来不是一蹴而就,需要多次的重温以及结合实际来练习,才会让你记忆更加深刻。下面我们就开始学习相关的知识吧。

redux的基本执行流程

以下是三个redux执行流程的图,要是对redux执行流程仍然不太清楚的同学,可以参照看看,希望对你们有帮助

20301380fb59ec69625d32e5daf2cba2.gif

9f58081fb0e5cc041fa73490f6e0a096.jpeg

3a598ecc71911f5e785cfb93be32f771.jpeg

目录分析

redux源码使用ts写的,但是我们为了学习redux,没有必要过多关注与功能无关的一些知识,所以我是把编译后redux的代码拿过来直接学习。

iShot2021-12-26 21.09.31.png 这里我们无需关心utils里的方法,我们只需要关注createStore,combineReducer,applyMiddleware,bindActionCreators,compose这五个文件。其中我们需要重点关注的是前三个文件。 首先我们来看createStore

createStore

import $$observable from './utils/symbol-observable'

import ActionTypes from './utils/actionTypes'
import isPlainObject from './utils/isPlainObject'
import { kindOf } from './utils/kindOf'

/**
 * 创建一个redux的store来保存state的状态树
 * 要改变store唯一的方法就是调用dispatch()
 * 你的应用里面只有一个store,你的应用里面里面可能有很多的模块,你可以有很多个reducer,最后在root reducer中使用combineReducers方法把他们合并起来。
 * @param {Function} reducer type:function
 *    这个函数会接受当前state树的状态,返回下一个state树的状态。
 * @param {any} [preloadedState] type:undefined | object
 *    初始化的state。如果是你在有服务端渲染的app使用,你可以把服务端的状态放进初始化的state中。或者你可以恢复以前的状态
 * @param {Function} [enhancer]  type:undefined | function
 *    store的增强器。你可以使用第三方的一些插件来增强存储,时间旅行,持久化等功能。
 *    Redux自带的唯一可以增强的方就是applyMiddleware()
 * @returns {Store} 返回值 就是一个store
 *
 */
export default function createStore(reducer, preloadedState, enhancer) {
  //如果传递的preloadedState和enhancer都是函数,而不是对象的话,或者enhancer有多个的话
  //那么就抛出一个异常
  if (
    (typeof preloadedState === 'function' && typeof enhancer === 'function') ||
    (typeof enhancer === 'function' && typeof arguments[3] === 'function')
  ) {
    throw new Error(
      'It looks like you are passing several store enhancers to ' +
        'createStore(). This is not supported. Instead, compose them ' +
        'together to a single function. See https://redux.js.org/tutorials/fundamentals/part-4-store#creating-a-store-with-enhancers for an example.'
    )
  }
  //如果preloadedState是函数且enhancer是undefined
  //那么就认为没有传递preloadedState,就把preloadedState赋值给enhancer,把undefined赋值给preloadedState
  if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
    enhancer = preloadedState
    preloadedState = undefined
  }
  //如果enhancer不等于undefined但也不是函数,那就抛出异常。enhancer的类型是undefined或者function
  // 如果enhancer存在,那他必须是个function, 否则throw Error
  if (typeof enhancer !== 'undefined') {
    if (typeof enhancer !== 'function') {
      throw new Error(
        `Expected the enhancer to be a function. Instead, received: '${kindOf(
          enhancer
        )}'`
      )
    }
    /**
     * 传入符合参数类型的参数,就可以执行 enhancer,
     * 但是这个return深深的吸引了我, 因为说明有applyMiddleware的时候后面的都不用看了 ??? 当然不可能
     * 可是applyMiddleware其实是必用项,所以猜想一下applyMiddleware强化store之后会enhancer赋值undefined,再次调用createStore
     * 上下打个debugger看一下执行顺序(debugger位置以注释),果然不出所料
     * 好了, 假设我们还不知道applyMiddleware()这个funcrion具体干了什么,
     * 只知道他做了一些处理然后重新调用了createStore并且enhancer参数为undefined
     * 先记下,后续在看applyMiddleware, 因为我们现在要看的是createStore
     * * */
    return enhancer(createStore)(reducer, preloadedState)
  }

  //如果enhancer不存在
  if (typeof reducer !== 'function') {
    throw new Error(
      `Expected the root reducer to be a function. Instead, received: '${kindOf(
        reducer
      )}'`
    )
  }

  let currentReducer = reducer //当前的reducer
  let currentState = preloadedState //初始化当前的state
  let currentListeners = []// 当前有哪些监听器
  let nextListeners = currentListeners
  let isDispatching = false // 标志dispatch是否正在执行中

  /**
   * 这将创建currentListeners的浅拷贝,以便我们可以在dispatch时候,nextListeners作为临时列表。
   * 这可以防止消费者dispatch时出现任何错误在调度的中间订阅/取消订阅。
   * 其实这里是保存一份订阅快照
   */
  function ensureCanMutateNextListeners() {
    //  不要忘了let nextListeners = currentListeners // 浅拷贝下这个队列
    // 判断nextListeners和当前的currentListeners是不是一个引用
    if (nextListeners === currentListeners) {
      // 如果是一个引用的话深拷贝出来一个currentListeners赋值给nextListener
      nextListeners = currentListeners.slice()
    }
  }

  /**
   *  读取状态树
   * @returns {any} 返回值是当前的状态树
   */
  function getState() {
    if (isDispatching) {
      throw new Error(
        'You may not call store.getState() while the reducer is executing. ' +
          'The reducer has already received the state as an argument. ' +
          'Pass it down from the top reducer instead of reading it from the store.'
      )
    }

    return currentState
  }

  /**
   * @param {Function} listener 每次dispatch的函数
   * @returns {Function} 返回值是移除监听器的函数
   *
   * 订阅listener监听器。
   * 它会在dispatch一个action或者状态树被改变的情况下被调用。你可以调用getState()这个方法去读取当前的状态树
   * 你可以在一个要改变的listener监听器中调用一个dispatch函数
   * 以下是注意事项:
   *  1、订阅是在每次调用dispatch()方法之前进行快照的。如果在调用监听器时,订阅或者取消订阅,都不会对当前进行的dispatch产生任何影响
   *  但是,对于下一个dispatch,不论是否嵌套或者其它的一些条件,都会使用订阅列表的最近的快照。
   *  2、监听器不应该期望看到偶有的状态都发生变更。因为在调用监听器前,在嵌套的dispatch()中,状态可能已经多次进行改变了。
   *  但是,可以保证dispatch 启动之前注册的所有订阅者在退出时都将会以最新的状态调用。
   */
  // store.subscribe方法设置监听函数,一旦触发dispatch,就自动执行这个函数
  // listener是一个callback function
  function subscribe(listener) {
    if (typeof listener !== 'function') {
      throw new Error(
        `Expected the listener to be a function. Instead, received: '${kindOf(
          listener
        )}'`
      )
    }

    if (isDispatching) {
      throw new Error(
        'You may not call store.subscribe() while the reducer is executing. ' +
          'If you would like to be notified after the store has been updated, subscribe from a ' +
          'component and invoke store.getState() in the callback to access the latest state. ' +
          'See https://redux.js.org/api/store#subscribelistener for more details.'
      )
    }

    let isSubscribed = true//标志是否已经订阅

    ensureCanMutateNextListeners() //把当前的监听器浅拷贝一份nextListeners
    nextListeners.push(listener)

    //返回移除监听器的函数
    return function unsubscribe() {
      if (!isSubscribed) {
        return
      }

      if (isDispatching) {
        throw new Error(
          'You may not unsubscribe from a store listener while the reducer is executing. ' +
            'See https://redux.js.org/api/store#subscribelistener for more details.'
        )
      }

      isSubscribed = false //取消已经订阅的标志

      ensureCanMutateNextListeners()//把当前的监听器浅拷贝一份nextListeners
      //从这个列表中删除这个监听器
      const index = nextListeners.indexOf(listener)
      nextListeners.splice(index, 1)
      currentListeners = null
    }
  }

  /**
   * 抛发一个action 这是触发状态更改唯一的方法
   * 这个reducer函数会使用当前的状态树,以及会调用给定的action。它的返回值将会通知下state树的下一个状态并且它会改变这个监听器。
   * 目前只支持普通对象操作,如果需要抛出promise或者Observable,你需要引入第三方的的中间件去包裹。
   * @param {Object} action 它表示更改的纯对象,它可以保持操作的序列化,也可以配合devTools做时间旅行
   * @returns {Object} 为方便起见,返回值与你操作的action相同
   */
  function dispatch(action) {
    // 看下util的isPlainObject
    // acticon必须是由Object构造的函数, 否则throw Error
    if (!isPlainObject(action)) {
      throw new Error(
        `Actions must be plain objects. Instead, the actual type was: '${kindOf(
          action
        )}'. You may need to add middleware to your store setup to handle dispatching other values, such as 'redux-thunk' to handle dispatching functions. See https://redux.js.org/tutorials/fundamentals/part-4-store#middleware and https://redux.js.org/tutorials/fundamentals/part-6-async-logic#using-the-redux-thunk-middleware for examples.`
      )
    }

    if (typeof action.type === 'undefined') {
      throw new Error(
        'Actions may not have an undefined "type" property. You may have misspelled an action type string constant.'
      )
    }

    if (isDispatching) {
      throw new Error('Reducers may not dispatch actions.')
    }

    try {
      isDispatching = true //首先更改是否在dispatch的状态
      //执行reducer去更改state,处理完这个state之后,就返回状态树,并赋值给currentState
      currentState = currentReducer(currentState, action)
    } finally {
      isDispatching = false
    }
    //状态改变之后,先去把nextListeners赋值给currentListeners,
    // 然后遍历执行每一个监听器,每个监听器都是一个函数。
    // 监听队列
    // 所有的的监听函数赋值给 listeners
    const listeners = (currentListeners = nextListeners)
    for (let i = 0; i < listeners.length; i++) {
      const listener = listeners[i]
      listener()
    }
    // 到这里dispatch方法就结束了, 我们来思考总结一下, 为什么要用listeners
    // 当dispatch发送一个规范的action时,会更新state
    // 但是state改变了之后我们需要做一些事情, 比如更新ui既数据驱动视图
    // (当然一般我们使用react,react-redux的时候, 他们会帮我们完成这些事情)
    // 所以要提供一个监听模式,当然还要有一个监听函数subscribe, 保证dispatch和subscribe之间的一对多的模式
    return action
  }

  /**
   * 用来替换当前计算的store的reducer
   * redux 热加载机制的时候用到这个函数
   * @param {Function} nextReducer 下一个reducer
   * @returns {void}
   */
  function replaceReducer(nextReducer) {
    //如果nextReducer不是函数,那就抛出异常。
    if (typeof nextReducer !== 'function') {
      throw new Error(
        `Expected the nextReducer to be a function. Instead, received: '${kindOf(
          nextReducer
        )}`
      )
    }

    currentReducer = nextReducer

    //此操作的效果与ActionType.INIT类似。
    // 新rootReducer和旧rootReducer中存在的任何reducer都将接收先前的状态。这将使用旧状态树中的任何相关数据有效地填充新状态树。
    dispatch({ type: ActionTypes.REPLACE })
  }

  function observable() {
    const outerSubscribe = subscribe
    return {
      /**
       * 最小的可观测的方法
       * observer参数是可以用作观察者的一个对象
       * 这个observer独享应该有一个next的方法
       * 返回值是一个可以用于从store中去取消订阅可观察内容,并且可以防止进一步从可观察对象发射值的对象
       */
      subscribe(observer) {
        if (typeof observer !== 'object' || observer === null) {
          throw new TypeError(
            `Expected the observer to be an object. Instead, received: '${kindOf(
              observer
            )}'`
          )
        }

        function observeState() {
          if (observer.next) {
            observer.next(getState())
          }
        }

        observeState()
        const unsubscribe = outerSubscribe(observeState)
        return { unsubscribe }
      },

      [$$observable]() {
        return this
      },
    }
  }

  // 创建存储时,将调度一个“INIT”的action,以便reducer返回其初始状态。这将有效地填充初始状态树。
  // 有没有想过,在使用redux的时候, 初始化的state哪来的
  // 当然是自己先dispatch了一下
  //reducer 返回其初始状态
  //初始化 store 里的 state tree
  dispatch({ type: ActionTypes.INIT })

  return {
    dispatch,
    subscribe,
    getState,
    replaceReducer,
    [$$observable]: observable,
  }
}

combineReducer

import ActionTypes from './utils/actionTypes'
import warning from './utils/warning'
import isPlainObject from './utils/isPlainObject'
import { kindOf } from './utils/kindOf'

//不看 与功能无关
function getUnexpectedStateShapeWarningMessage(
  inputState,
  reducers,
  action,
  unexpectedKeyCache
) {
  const reducerKeys = Object.keys(reducers)
  const argumentName =
    action && action.type === ActionTypes.INIT
      ? 'preloadedState argument passed to createStore'
      : 'previous state received by the reducer'

  if (reducerKeys.length === 0) {
    return (
      'Store does not have a valid reducer. Make sure the argument passed ' +
      'to combineReducers is an object whose values are reducers.'
    )
  }

  if (!isPlainObject(inputState)) {
    return (
      `The ${argumentName} has unexpected type of "${kindOf(
        inputState
      )}". Expected argument to be an object with the following ` +
      `keys: "${reducerKeys.join('", "')}"`
    )
  }

  const unexpectedKeys = Object.keys(inputState).filter(
    (key) => !reducers.hasOwnProperty(key) && !unexpectedKeyCache[key]
  )

  unexpectedKeys.forEach((key) => {
    unexpectedKeyCache[key] = true
  })

  if (action && action.type === ActionTypes.REPLACE) return

  if (unexpectedKeys.length > 0) {
    return (
      `Unexpected ${unexpectedKeys.length > 1 ? 'keys' : 'key'} ` +
      `"${unexpectedKeys.join('", "')}" found in ${argumentName}. ` +
      `Expected to find one of the known reducer keys instead: ` +
      `"${reducerKeys.join('", "')}". Unexpected keys will be ignored.`
    )
  }
}
//不看 与功能无关
function assertReducerShape(reducers) {
  Object.keys(reducers).forEach((key) => {
    const reducer = reducers[key]
    // reducer返回值
    const initialState = reducer(undefined, { type: ActionTypes.INIT })

    if (typeof initialState === 'undefined') {
      throw new Error(
        `The slice reducer for key "${key}" returned undefined during initialization. ` +
          `If the state passed to the reducer is undefined, you must ` +
          `explicitly return the initial state. The initial state may ` +
          `not be undefined. If you don't want to set a value for this reducer, ` +
          `you can use null instead of undefined.`
      )
    }
    // 很明显assertReducerShape是用于reducer的规范
    // 回到combineReducers
    if (
      typeof reducer(undefined, {
        type: ActionTypes.PROBE_UNKNOWN_ACTION(),
      }) === 'undefined'
    ) {
      throw new Error(
        `The slice reducer for key "${key}" returned undefined when probed with a random type. ` +
          `Don't try to handle '${ActionTypes.INIT}' or other actions in "redux/*" ` +
          `namespace. They are considered private. Instead, you must return the ` +
          `current state for any unknown actions, unless it is undefined, ` +
          `in which case you must return the initial state, regardless of the ` +
          `action type. The initial state may not be undefined, but can be null.`
      )
    }
  })
}

// 用于合并reducer 一般是这样combineReducers({a,b,c})
export default function combineReducers(reducers) {
  // reducers中key的数组
  const reducerKeys = Object.keys(reducers)
  // 最终的reducer
  const finalReducers = {}
  for (let i = 0; i < reducerKeys.length; i++) {
    // 接受当前的key
    const key = reducerKeys[i]
    // 如果不是生产环境, 当前的reducer是undefined会给出warning
    if (process.env.NODE_ENV !== 'production') {
      if (typeof reducers[key] === 'undefined') {
        warning(`No reducer provided for key "${key}"`)
      }
    }
    // reducer要是一个function
    if (typeof reducers[key] === 'function') {
      // 赋值给finalReducers
      finalReducers[key] = reducers[key]
    }
    // 循环结束, 目的为了给finalReducers赋值, 过虑了不符合规范的reudcer
  }
  // 符合规范的reducer的key数组
  const finalReducerKeys = Object.keys(finalReducers)

  // This is used to make sure we don't warn about the same
  // keys multiple times.
  // 意想不到的key, 先往下看看
  let unexpectedKeyCache
  if (process.env.NODE_ENV !== 'production') {
    // production环境为{}
    unexpectedKeyCache = {}
  }

  let shapeAssertionError
  try {
    // 看这个function
    assertReducerShape(finalReducers)
  } catch (e) {
    shapeAssertionError = e
  }
  // 返回function, 这就是createstore中的reducer参数,reducer参数也就是currentreducer参数
  // 自然有state和action两个参数, 可以回createstore文件看看currentReducer(currentState, action)
  return function combination(state = {}, action) {
    // reducer不规范报错
    if (shapeAssertionError) {
      throw shapeAssertionError
    }
    // 比较细致的❌信息,顺便看了一下getUndefinedStateErrorMessage,都是用于提示warning和error的, 不过多解释了
    if (process.env.NODE_ENV !== 'production') {
      const warningMessage = getUnexpectedStateShapeWarningMessage(
        state,
        finalReducers,
        action,
        unexpectedKeyCache
      )
      if (warningMessage) {
        warning(warningMessage)
      }
    }

    let hasChanged = false
    const nextState = {} //保存新state的对象
    for (let i = 0; i < finalReducerKeys.length; i++) {
      // 获取finalReducerKeys的key和value(function)
      const key = finalReducerKeys[i]
      const reducer = finalReducers[key]
      // 当前key的state值
      const previousStateForKey = state[key]
      // 执行reducer, 返回执行reducer完毕之后新的state
      const nextStateForKey = reducer(previousStateForKey, action)
      // 不存在返回值报错
      if (typeof nextStateForKey === 'undefined') {
        const actionType = action && action.type
        throw new Error(
          `When called with an action of type ${
            actionType ? `"${String(actionType)}"` : '(unknown type)'
          }, the slice reducer for key "${key}" returned undefined. ` +
            `To ignore an action, you must explicitly return the previous state. ` +
            `If you want this reducer to hold no value, you can return null instead of undefined.`
        )
      }
      // 新的state放在nextState对应的key里
      nextState[key] = nextStateForKey
      // 判断新的state是不是同一引用, 以检验reducer是不是纯函数
      hasChanged = hasChanged || nextStateForKey !== previousStateForKey
    }
    //hasChanged如果为false,判断最终合并的reducer的key的数量是不是与state中key的数量是不是相等
    hasChanged =
      hasChanged || finalReducerKeys.length !== Object.keys(state).length

    //如果hasChanged存在就返回下一个state,如果不存在,就返回当前的state
    return hasChanged ? nextState : state
  }
}

bindActionCreator

import { kindOf } from './utils/kindOf'

function bindActionCreator(actionCreator, dispatch) {
  // 闭包
  return function () {
    // 执行后返回结果为传入的actionCreator直接调用arguments
    return dispatch(actionCreator.apply(this, arguments))
  }
}

/**
 * @export
 * @param {*} actionCreators  一个 action creator,或者一个 value 是 action creator 的对象。
 * @param {*} dispatch 一个由 Store 实例提供的 dispatch 函数。
 * @returns 一个与原对象类似的对象,只不过这个对象的 value 都是会直接 dispatch 原 action creator 返回的结果的函数。
 *          如果传入一个单独的函数作为 actionCreators,那么返回的结果也是一个单独的函数。
 *
 * 场景: 惟一会使用到 bindActionCreators 的场景是当你需要把 action creator 往下传到一个组件上,
 *        却不想让这个组件觉察到 Redux 的存在,而且不希望把 dispatch 或 Redux store 传给它。
 **/
export default function bindActionCreators(actionCreators, dispatch) {
  // actionCreators为function
  if (typeof actionCreators === 'function') {
    return bindActionCreator(actionCreators, dispatch)
  }

  if (typeof actionCreators !== 'object' || actionCreators === null) {
    throw new Error(
      `bindActionCreators expected an object or a function, but instead received: '${kindOf(
        actionCreators
      )}'. ` +
        `Did you write "import ActionCreators from" instead of "import * as ActionCreators from"?`
    )
  }

  const boundActionCreators = {}
  //遍历整个actionCreators
  //如果item是function类型,那么就调用bindActionCreator方法
  for (const key in actionCreators) {
    const actionCreator = actionCreators[key]
    if (typeof actionCreator === 'function') {
      boundActionCreators[key] = bindActionCreator(actionCreator, dispatch)
    }
  }
  return boundActionCreators
}

compose

因为后面的applyMiddleware用到了compose方法,所以我们先看compose,最后再看applyMiddleware

/**
 * Composes single-argument functions from right to left. The rightmost
 * function can take multiple arguments as it provides the signature for
 * the resulting composite function.
 *
 * @param {...Function} funcs The functions to compose.
 * @returns {Function} A function obtained by composing the argument functions
 * from right to left. For example, compose(f, g, h) is identical to doing
 * (...args) => f(g(h(...args))).
 */
//组合函数,将函数串联起来从右到左执行
//compose(funcA, funcB, funcC) === compose(funcA(funcB(funcC())))
export default function compose(...funcs) {
  //如果传入函数数组为空,就直接返回一个空的函数
  if (funcs.length === 0) {
    return (arg) => arg
  }
 //如果传入函数数组只有一个函数,就直接返回它函数
  if (funcs.length === 1) {
    return funcs[0]
  }
  //自己展开 更容易看懂,其实就是科里化函数
  return funcs.reduce((a,b)=>{
    return (...args)=>{
      return a(b(...args));
    }
  })
  //官方实现
  // return funcs.reduce((a, b) => (...args) => a(b(...args)))
}

appleMiddleware

import compose from './compose'

/***
 *
 * middleware就是中间件,简单说在redux中作为扩展 dispatch 的唯一标准的方式。
 * 不熟悉的同学自行去api了解一下, 大致结构是这样的
 *  middleware = (store) => (next) =>(action) =>{ [return next(action)]}
 */
// applyMiddleware用来添加中间件,在修改数据的时候redux通过改造dispatch来实现中间件.
export default function applyMiddleware(...middlewares) {
  // 返回一个名为createStore的function
  // 不知道你还是否记得createStore.js开头的这段代码
  /*
    if (typeof enhancer !== 'undefined') {
    if (typeof enhancer !== 'function') {
      throw new Error('Expected the enhancer to be a function.')
    }
    return enhancer(createStore)(reducer, preloadedState)
    }
    嗯哼?对上了吧, 有applyMiddleware的时候直接先执行这里, 没绕过来的同学debugger一下
  * */
  /**
   *   createStore.js
   *   d1 = createStore(reducer, initstate, enhancer){ ... debugger if (typeof enhancer !== 'undefined')}
   *
   *   d2 =  if (typeof enhancer !== 'undefined') {
                      if (typeof enhancer !== 'function') {
                        throw new Error('Expected the enhancer to be a function.')
                      }
                      debugger
                    return enhancer(createStore)(reducer, preloadedState)
                    }
   *   d3 = if (typeof enhancer !== 'undefined') {} debugger
   *
   *   d4 =  ... debugger const middlewareAPI = {
                      // copy getState
                      getState: store.getState,
                      dispatch: (...args) => dispatch(...args)
                    }

   d5 =    ... debugger  const store = createStore(...args)...
   *
   *   执行顺序
   *   创建store的首先是调用createStore(...applyMiddleware()) 大致发生了这样的流程
   *   createStore(...applyMiddleware()) -> applyMiddleware() -> return function -> d1 -> d2
   *   接下来
   *   return enhancer(createStore)(reducer, preloadedState) -> d5 -> createStore(...args)再次调用createStore -> d1
   *   接下来走d3下面的store初始化 -> dispatch(init) -> d4 -> 组合middleware,合并new dispatch -> 返回增强的store
   */
  return (createStore) => (...args) => {
    // 保存createStore(reducer, initstate) || createStore(reducer), 赋值给store
    const store = createStore(...args)
    // 定义了一个dispatch, 调用会 throw new Error(dispatching虽然构造middleware但不允许其他middleware应用
    let dispatch = () => {
      throw new Error(
        'Dispatching while constructing your middleware is not allowed. ' +
          'Other middleware would not be applied to this dispatch.'
      )
    }
    // 定义middlewareAPI, 中间件中的store  eg ->  logger(store)
    const middlewareAPI = {
      getState: store.getState,
      // 添加dispatch并包装一个function, 参数为(reducer, [initstate])
      // 向下看一看middlewareAPI作为参数被回调回去,不难理解, 告诉dispath不能再middleware插件中构造
      dispatch: (...args) => dispatch(...args),
    }
    // 调用每一个这样形式的middleware = store => next => action =>{},
    // 组成一个这样[f(next)=>acticon=>next(action)...]的array,赋值给chain
    const chain = middlewares.map((middleware) => middleware(middlewareAPI))
    // compose(...chain)会形成一个调用链, next指代下一个函数的注册, 这就是中间件的返回值要是next(action)的原因
    // 如果执行到了最后next就是原生的store.dispatch方法
    dispatch = compose(...chain)(store.dispatch)
    // 返回增强的store
    return {
      ...store,
      dispatch,
    }
  }
}

问题

Q: dispatch 之后,Redux 是如何去处理的?state 中的数据被修改之后,页面如何去收到更新后的数据? A: 首先会利用当前的 reducer、state 以及传入的参数 action 得到新的 state, 然后通过触发监听数组中的函数,让函数中的使用的 store.getState() 再次触发,起到通知数据更新的作用。

Q:为什么一个临时变量 dispatch 被赋值了 2 次? A:首先从第一个变量返回的 throw Error 可以看出这段代码希望在 middleware 数组被构建时, dispatch 不应该被调用,否则抛错。而在 middlewareAPI 的 dispatch 中被调用了一次但没触发这个 throw Error,是因为其实直到给 dispatch 第二次赋值时才真正调用 dispatch(我们之前解读到直到 compose 函数传入了(store.dispatch) 之后才会触发调用),所以这时 middlewareAPI 的 dispatch 并不会触发。

Q:middlewareAPI 的 dispatch 为什么要用匿名函数包裹? A:目的就是如果每个 middleware 对 dispatch 有所改变,middleware 里面的 dispatch 也会相应做出改变(如一问中所说,compose(...chain)(store.dispatch) 触发了 middlewareAPI 的 dispatch 被调用)。

结尾

本文参考了一些文章: