初步解读 Redux 源码理解其原理

Redux github 地址

前言

Redux 源码的体量算是比较小的了,但是详细解读还是有点压力,在此简单解读下,目的是能大概理解其原理。直接看导出的 API:createStorecombineReducersbindActionCreatorsapplyMiddlewarecompose,其中 bindActionCreators 暂时不想理会,余下的依次介绍。

createStore

关键代码(省去一些加强健壮性的代码)以及对应的解读注释如下:

// 函数接受三个参数,第一个是 reducer,第二个是初始 state,第三个是由 applyMiddleware
// 生成的 enhancer
export default function createStore(reducer, preloadedState, enhancer) {
  if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
    enhancer = preloadedState
    preloadedState = undefined
  }

  if (typeof enhancer !== 'undefined') {
    if (typeof enhancer !== 'function') {
      throw new Error('Expected the enhancer to be a function.')
    }

    return enhancer(createStore)(reducer, preloadedState) // 可以看出 enhancer
    // 是一个以 createStore 为参数然后返回一个 createStore 的高阶函数,待会儿通过
    // applyMiddleware 可以看到这个 enhancer 到底是怎么起作用的
  }
  let currentReducer = reducer
  let currentState = preloadedState
  let currentListeners = []
  let nextListeners = currentListeners
  let isDispatching = false

  function ensureCanMutateNextListeners() {
    if (nextListeners === currentListeners) {
      nextListeners = currentListeners.slice()
    }
  }

  function getState() { // 返回当前的 state
    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
  }

  function subscribe(listener) { // 订阅函数,每当 dispatch 的时候 listeners 都会执行
    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-reference/store#subscribe(listener) for more details.'
      )
    }

    let isSubscribed = true

    ensureCanMutateNextListeners()
    nextListeners.push(listener)

    // 返回值是一个可以取消当前 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-reference/store#subscribe(listener) for more details.'
        )
      }

      isSubscribed = false

      ensureCanMutateNextListeners()
      const index = nextListeners.indexOf(listener)
      nextListeners.splice(index, 1)
    }
  }

  // dispatch,通过这个函数 dispatch action 借由 reducers 来生成一个新的 state
  function dispatch(action) {
    if (!isPlainObject(action)) {
      throw new Error(
        'Actions must be plain objects. ' +
          'Use custom middleware for async actions.'
      )
    }

    if (typeof action.type === 'undefined') {
      throw new Error(
        'Actions may not have an undefined "type" property. ' +
          'Have you misspelled a constant?'
      )
    }

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

    try {
      isDispatching = true
      currentState = currentReducer(currentState, action)
    } finally {
      isDispatching = false
    }

    const listeners = (currentListeners = nextListeners)
    for (let i = 0; i < listeners.length; i++) { // 如前所述,每 dispatch 一个 action 时
      // 都会去执行所有的 listeners
      const listener = listeners[i]
      listener()
    }

    return action // 返回值和传入的参数一样,action
  }

  function replaceReducer(nextReducer) { // 替换 reducer
    if (typeof nextReducer !== 'function') {
      throw new Error('Expected the nextReducer to be a function.')
    }

    currentReducer = nextReducer
    // replace reducer 后 dispatch  一个 REPLACE action
    dispatch({ type: ActionTypes.REPLACE })
  }

  // When a store is created, an "INIT" action is dispatched so that every
  // reducer returns their initial state. This effectively populates
  // the initial state tree.
  // store 生成后 dispatch 一个 INIT 的 action
  dispatch({ type: ActionTypes.INIT })

  return {
    dispatch,
    subscribe,
    getState,
    replaceReducer
  }
}

createStore 主要的作用就是根据提供的 reducerinitialStateenhancer 生成 store,然后 store 可以提供 dispatchsubscribegetStatereplaceReducer等方法。

combineReducers

combineReducer 的作用是将多个(如果有的话) reducer 整合成一个总的 reducer,关键代码:

// reducers 是一个 plain object,一个 key 对应一个 reducer
export default function combineReducers(reducers) {
  const reducerKeys = Object.keys(reducers)
  const finalReducers = {}
  for (let i = 0; i < reducerKeys.length; i++) {
    const key = reducerKeys[i]

    // 过滤无效的 reducer
    if (typeof reducers[key] === 'function') {
      finalReducers[key] = reducers[key]
    }
  }
  const finalReducerKeys = Object.keys(finalReducers)

  // 返回值依然是一个以 state 和 action 为参数的 reducer,但是它可以处理所有的 type 的 action
  return function combination(state = {}, action) {

    let hasChanged = false
    const nextState = {}
    // 具体处理 action 的做法是,把每个 action 代入所有 reducer 生成对应的结果,然后再整合
    for (let i = 0; i < finalReducerKeys.length; i++) {
      const key = finalReducerKeys[i]
      const reducer = finalReducers[key]
      const previousStateForKey = state[key]
      const nextStateForKey = reducer(previousStateForKey, action)

      nextState[key] = nextStateForKey
      hasChanged = hasChanged || nextStateForKey !== previousStateForKey
    }
    return hasChanged ? nextState : state
  }
}

compose

这个方法的代码最少:

export default function compose(...funcs) {
  if (funcs.length === 0) {
    return arg => arg
  }

  if (funcs.length === 1) {
    return funcs[0]
  }

  return funcs.reduce((a, b) => (...args) => a(b(...args)))
}

funcs是一个函数数组,reduce是数组的归并方法。这个方法接受一系列的函数作为参数,而且这一系列函数,从右到左,上一个函数的返回值可以为下一个函数的参数,最终返回一个以最右边的函数的参数为参数(这个参数再依次交给左边的函数处理,返回后继续此步骤,一直到最左边)以最左边的函数的返回值为返回值的复合函数。比如:

 const F = compose(f1, f2. f3, f4, ..., fn)
 F //f1(f2...(fn(...args)))

applyMiddleware

applyMiddleware,可以说这个方法为 Redux 提供了各种可能性,关键代码:

import compose from './compose'

export default function applyMiddleware(...middlewares) {
  // 返回值是一个同时以 createStore 为参数和返回值的闭包
  return createStore => (...args) => {
    const store = createStore(...args) // 这里的store与没有enhancer时的并无二致
    let dispatch = () => {
      throw new Error( // 中间件中不允许 dispatch
        `Dispatching while constructing your middleware is not allowed. ` +
          `Other middleware would not be applied to this dispatch.`
      )
    }
    
    // 中间件API,规定了 middleware 是一个以 { getState, dispatch } 为参数的函数
    const middlewareAPI = {
      getState: store.getState,
      dispatch: (...args) => dispatch(...args)
    }

    // chain 的每个元素依然是一个函数,经过 compose 作用后返回一个合成函数,合成函数以
    // store.dispatch 为参数,最终生成一个加强了的 dispatch
    const chain = middlewares.map(middleware => middleware(middlewareAPI))
    dispatch = compose(...chain)(store.dispatch)
    // 上面语句分开写就是 const composedFn = compose(...chain)
    // dispatch = composedFn(store.dispatch) 
    // 其中 composedFn: (...args) => chain[0](chain[1](...(chain[length - 1](...args))))
    // 可以看到,`createStore` 经过 `storeEnhancer` 加强之后,其实只是用新的`dispatch` 将原来
    // 的 `dispatch` 替换,其他的部分保持不变
    return {
      ...store,
      dispatch
    }
  }
}

这个方法接受一系列的 Redux 的 middleware 为参数,然后返回一个以 createStore 为参数的 storeEnhancer,其实 storeEnhancer enhance 的是 dispatch(这里好像并不准确,因为除了由中间件生成的 storeEnhancer 以外,还有其他的 storeEnhancer,而这些 storeEnhancer 就有更强的功能,比如像 devToolsExtension 这样的扩展工具)。由于每个 middleware 在作用 { getState, dispatch } 后可以被 compose 处理,那我们可以知道 middleware({ getState, dispatch }) 的返回值是一个函数,而且这个函数的参数和返回值是具有相同签名的函数,于是 middleware 的函数签名大概是:({ getState, dispatch }) => next => action,其中 next(action) 表示将 action 交由下一个 middleware 处理,最后一个 middleware 的 nextdispatch

举个例子:

//m1, m2, m3 是三个中间件
const middlewares = [m1, m2, m3]

const storeEnhancer = applyMiddleWare(...middlewares)
const store = createStore(reducer, {}, storeEnhancer)

export default store

store.dispatch 被强化的过程是这样:
普通 dispatch -> 被 m3 强化后的 dispatch(记为 m3(dispatch)) -> 再被 m2 强化后的 dispatch(记为 m2(m3(dispatch))) -> 再被 m1 强化后的 dispatch(记为 m1(m2(m3(dispatch)))
对应地,加载了上述中间件的 store dispatch 一个 action 的过程是这样:
m1(m2(m3(dispatch)))(action) -> next(action)next === m2(m3(dispatch)))-> next(action)next === m3(dispacth))-> next(action)next === dispatch)。

你可能感兴趣的:(初步解读 Redux 源码理解其原理)