Skip to content

redux源码解读 #5

Description

@moyui

这是源码解读系列的第二篇,虽然说对于Redux已经有好多文章说的不能再详细了,但是Redux中有一些非常有意思的函数式编程技巧与发布——订阅者模式,这个设计模式对于目前的前端工程是核心,也是面试经常会考到的。我会把redux的注释简单翻译一下,并且其余地方加上我的理解。在这里特别感谢面试图谱以及cbbfcd同学,我借鉴了它们分析的要点,两位的链接我会放在最后。

redux源码分析,基于4.0.0版本

Componse

首先看一个公共函数Componse。

/**
 * 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))).
 */
 /**
  *从右至左组合单变量函数。为了它们(指单变量函数)组合后的函数的结果,最右边的函数可以调用多个参数作为它的签名(?)
  */

// 这个函数设计的很巧妙,通过传入函数引用的方式让我们完成多个函数的嵌套使用,术语叫做高阶函数
// 通常在redux中我们会这样调用
// compose(
//     applyMiddleware(thunkMiddleware),
//     window.devToolsExtension ? window.devToolsExtension() : f => f
// ) 
// 经过 compose 函数变成了 applyMiddleware(thunkMiddleware)(window.devToolsExtension()())
// 所以在找不到 window.devToolsExtension 时你应该返回一个函数
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)))
}
//若有f1,f2,f3三个函数加入
//第二次迭代,a变为f1,b变为f2
//第一次迭代后,a变为(...args) => {return f1(f2(...args))}
//b变为f3
//所以最后形成f1(f2(f3(args)))

CreateStore

/**
 * Creates a Redux store that holds the state tree.
 * The only way to change the data in the store is to call `dispatch()` on it.
 *
 * There should only be a single store in your app. To specify how different
 * parts of the state tree respond to actions, you may combine several reducers
 * into a single reducer function by using `combineReducers`.
 *创造一个store对应state tree,改变他的唯一方式是调用dispatch。app中应该只有一个store,你可以结合几个reducers来组合成。
 */

 export default function createStore(reducer, preloadedState, enhancer) {
    // 一般 preloadedState 用的少,判断类型,如果第二个参数是函数且没有第三个参数,就调换位置
    if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
      enhancer = preloadedState
      preloadedState = undefined
    }
    // 判断 enhancer 是否是函数,不是函数就抛出错误
    if (typeof enhancer !== 'undefined') {
      if (typeof enhancer !== 'function') {
        throw new Error('Expected the enhancer to be a function.')
      }
      // 类型没错的话,先执行 enhancer,然后再执行 createStore 函数
      //enhancer一般就是我们用componse之后组合的增强器。
      return enhancer(createStore)(reducer, preloadedState)
    }
    // 判断 reducer 是否是函数
    if (typeof reducer !== 'function') {
      throw new Error('Expected the reducer to be a function.')
    }
    // 当前 reducer 即你写入的所有reducer
    let currentReducer = reducer
    // 当前State
    let currentState = preloadedState
    // 当前监听函数数组
    let currentListeners = []
    // 这是一个很重要的设计,为的就是每次在遍历监听器的时候保证 currentListeners 数组不变
    // 可以考虑下只存在 currentListeners 的情况,如果我在某个 subscribe 中再次执行 subscribe
    // 或者 unsubscribe,这样会导致当前的 currentListeners 数组大小发生改变,从而可能导致索引出错。
    //即也是一种纯函数,保证不修改原数组
    let nextListeners = currentListeners
    // reducer 是否正在执行
    let isDispatching = false
    // 如果 currentListeners 和 nextListeners 相同,下个处理事件为当前事件的复制。
    function ensureCanMutateNextListeners() {
      if (nextListeners === currentListeners) {
        nextListeners = currentListeners.slice()
      }
    }

    //Store上的getState方法
    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
      }
    
    /**
     *有两个警告
     * 1.订阅只是一个在dispatch调用前的快照,如果你在监听器唤醒之前订阅或者解除订阅
     * 这不会影响正在dispatch的进度,然而,下一次dispatch之后,就会使用最近的订阅与解除订阅列表
     * 2.监听器不会探测到所有state的变化、state可能在监听器触发之前之前就已经被更新多次通过dispatch
     * 然而,能够保证所有的监听器在dispatch开始调用之前和最新的state之前注册
     * 主要应用在react-redux中监听数据变化
     */
    function subscribe(listener) {
        if (typeof listener !== 'function') {
          throw new Error('Expected the listener to be a function.')
        }
    
        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)
        //返回取消订阅函数
        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
          //找到下次处理事件数组中的index并移除
          ensureCanMutateNextListeners()
          const index = nextListeners.indexOf(listener)
          nextListeners.splice(index, 1)
        }
      }

      //dispatch触发函数
      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)//生成新的state
        } finally {
          isDispatching = false
        }
        //这边是将马上要调用的事件赋值并调用
        const listeners = (currentListeners = nextListeners)
        for (let i = 0; i < listeners.length; i++) {
          const listener = listeners[i]
          listener()
        }
    
        return action
      }
      //替换readucer用
      function replaceReducer(nextReducer) {
        if (typeof nextReducer !== 'function') {
          throw new Error('Expected the nextReducer to be a function.')
        }
    
        currentReducer = nextReducer
        dispatch({ type: ActionTypes.REPLACE })
      }
      //观察者
      function observable() {
        const outerSubscribe = subscribe
        return {
          /**
           * The minimal observable subscription method.
           * @param {Object} observer Any object that can be used as an observer.
           * The observer object should have a `next` method.
           * @returns {subscription} An object with an `unsubscribe` method that can
           * be used to unsubscribe the observable from the store, and prevent further
           * emission of values from the observable.
           */
          subscribe(observer) {
            if (typeof observer !== 'object' || observer === null) {
              throw new TypeError('Expected the observer to be an object.')
            }
    
            function observeState() {
              if (observer.next) {
                observer.next(getState())
              }
            }
    
            observeState()
            //传入的实质是监听器
            const unsubscribe = outerSubscribe(observeState)
            return { unsubscribe }
          },
    
          [$$observable]() {
            return this
          }
        }
      }
    
      // 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.
      dispatch({ type: ActionTypes.INIT })
    
      return {
        dispatch,
        subscribe,
        getState,
        replaceReducer,
        [$$observable]: observable
      }
    }

CombineReducers

// 传入一个 object
//在用的时候类似于{1:1,2:2}
export default function combineReducers(reducers) {
    // 获取该 Object 的 key 值
     const reducerKeys = Object.keys(reducers)
     // 过滤后的 reducers
     const finalReducers = {}
     // 获取每一个 key 对应的 value
     // 在开发环境下判断值是否为 undefined
     // 然后将值类型是函数的值放入 finalReducers
     for (let i = 0; i < reducerKeys.length; i++) {
       const key = reducerKeys[i]
   
       if (process.env.NODE_ENV !== 'production') {
         if (typeof reducers[key] === 'undefined') {
           warning(`No reducer provided for key "${key}"`)
         }
       }
   
       if (typeof reducers[key] === 'function') {
         finalReducers[key] = reducers[key]
       }
     }
     // 拿到过滤后的 reducers 的 key 值
     const finalReducerKeys = Object.keys(finalReducers)
     
     // 在开发环境下判断,保存不期望 key 的缓存用以下面做警告  
     let unexpectedKeyCache
     if (process.env.NODE_ENV !== 'production') {
       unexpectedKeyCache = {}
     }
       
     let shapeAssertionError
     try {
     // 该函数解析在下面
       assertReducerShape(finalReducers)
     } catch (e) {
       shapeAssertionError = e
     }
   // combineReducers 函数返回一个函数,也就是合并后的 reducer 函数
   // 该函数返回总的 state
   // 并且你也可以发现这里使用了闭包,函数里面使用到了外面的一些属性
     return function combination(state = {}, action) {
       if (shapeAssertionError) {
         throw shapeAssertionError
       }
       // 该函数解析在下面
       if (process.env.NODE_ENV !== 'production') {
         const warningMessage = getUnexpectedStateShapeWarningMessage(
           state,
           finalReducers,
           action,
           unexpectedKeyCache
         )
         if (warningMessage) {
           warning(warningMessage)
         }
       }
       // state 是否改变
       let hasChanged = false
       // 改变后的 state
       const nextState = {}
       for (let i = 0; i < finalReducerKeys.length; i++) {
       // 拿到相应的 key
         const key = finalReducerKeys[i]
         // 获得 key 对应的 reducer 函数
         const reducer = finalReducers[key]
         // state 树下的 key 是与 finalReducers 下的 key 相同的
         // 所以你在 combineReducers 中传入的参数的 key 即代表了 各个 reducer 也代表了各个 state
         const previousStateForKey = state[key]
         // 然后执行 reducer 函数获得该 key 值对应的 state
         const nextStateForKey = reducer(previousStateForKey, action)
         // 判断 state 的值,undefined 的话就报错
         if (typeof nextStateForKey === 'undefined') {
           const errorMessage = getUndefinedStateErrorMessage(key, action)
           throw new Error(errorMessage)
         }
         // 然后将 value 塞进去
         nextState[key] = nextStateForKey
         // 如果 state 改变
         hasChanged = hasChanged || nextStateForKey !== previousStateForKey
       }
       // state 只要改变过,就返回新的 state
       return hasChanged ? nextState : state
     }
   }
//核心思想是state与reducer是分开的,先检验reducer是否有效,在将每次的传入的reducer加上action生成state,与原来的state比较,返回state


   // 这是执行的第一个用于抛错的函数
function assertReducerShape(reducers) {
    // 将 combineReducers 中的参数遍历
      Object.keys(reducers).forEach(key => {
        const reducer = reducers[key]
        // 给他传入一个 action
        const initialState = reducer(undefined, { type: ActionTypes.INIT })
        // 如果得到的 state 为 undefined 就抛错
        if (typeof initialState === 'undefined') {
          throw new Error(
            `Reducer "${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.`
          )
        }
        // 再过滤一次,考虑到万一你在 reducer 中给 ActionTypes.INIT 返回了值
        // 传入一个随机的 action 判断值是否为 undefined
        const type =
          '@@redux/PROBE_UNKNOWN_ACTION_' +
          Math.random()
            .toString(36)
            .substring(7)
            .split('')
            .join('.')
        if (typeof reducer(undefined, { type }) === 'undefined') {
          throw new Error(
            `Reducer "${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.`
          )
        }
      })
    }
    
    function getUnexpectedStateShapeWarningMessage(
      inputState,
      reducers,
      action,
      unexpectedKeyCache
    ) {
      // 这里的 reducers 已经是 finalReducers
      const reducerKeys = Object.keys(reducers)
      const argumentName =
        action && action.type === ActionTypes.INIT
          ? 'preloadedState argument passed to createStore'
          : 'previous state received by the reducer'
      
      // 如果 finalReducers 为空
      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.'
        )
      }
        // 如果你传入的 state 不是对象
      if (!isPlainObject(inputState)) {
        return (
          `The ${argumentName} has unexpected type of "` +
          {}.toString.call(inputState).match(/\s([a-z|A-Z]+)/)[1] +
          `". Expected argument to be an object with the following ` +
          `keys: "${reducerKeys.join('", "')}"`
        )
      }
        // 将参入的 state 于 finalReducers 下的 key 做比较,过滤出多余的 key
      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
    
    // 如果 unexpectedKeys 有值的话
      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.`
        )
      }
    }

ApplyMiddleware

//通常middleware是一个数组,所以调用这个函数应该这样写 
//applyMiddleware(...middlewares)(createStore)(...args)
export default function applyMiddleware(...middlewares) {
    return createStore => (...args) => {
    // 这里执行 createStore 函数,把 applyMiddleware 函数最后次调用的参数传进来
      const store = createStore(...args)
      let dispatch = () => {
        throw new Error(
          `Dispatching while constructing your middleware is not allowed. ` +
            `Other middleware would not be applied to this dispatch.`
        )
      }
   // 每个中间件都应该有这两个函数
      const middlewareAPI = {
        getState: store.getState,
        dispatch: (...args) => dispatch(...args)
      }
      // 把 middlewares 中的每个中间件都传入 middlewareAPI
      const chain = middlewares.map(middleware => middleware(middlewareAPI))
       // 和之前一样,从右至左调用每个中间件,然后传入 store.dispatch
      dispatch = compose(...chain)(store.dispatch)
  
      return {
        ...store,
        dispatch
      }
    }
  }

BindActionCreators

function bindActionCreator(actionCreator, dispatch) {
    return function() {
      return dispatch(actionCreator.apply(this, arguments))
    }
  }
  
  /**
   * Turns an object whose values are action creators, into an object with the
   * same keys, but with every function wrapped into a `dispatch` call so they
   * may be invoked directly. This is just a convenience method, as you can call
   * `store.dispatch(MyActionCreators.doSomething())` yourself just fine.
   *
   * For convenience, you can also pass a single function as the first argument,
   * and get a function in return.
   *转换一个值为action creators的对象,转换成有着相同keys的对象,但是每个函数都被一个dispatch调用包裹着,为了能够让它们直接唤醒。这个只是一个便利的方法。你可以直接`store.dispatch(MyActionCreators.doSomething())` 这样调用。
   */  
  export default function bindActionCreators(actionCreators, dispatch) {
    
    //如果第一个参数actionCreators为function类型  
    //把actionCreators,dispatch传入bindActionCreator函数并返回
    if (typeof actionCreators === 'function') {
      return bindActionCreator(actionCreators, dispatch);
    }
      
    //如果第actionCreators不是object类型 或者是null 则抛出错误
    if (typeof actionCreators !== 'object' || actionCreators === null) {
      throw new Error('bindActionCreators expected an object or a function, instead received ' + (actionCreators === null ? 'null' : typeof actionCreators) + '. ' + 'Did you write "import ActionCreators from" instead of "import * as ActionCreators from"?');
    }
    
    // 遍历得到所有key
    var keys = Object.keys(actionCreators);
    var boundActionCreators = {};
    
    //注入dispatch与actionCreator
    for (var i = 0; i < keys.length; i++) {
      var key = keys[i];
      var actionCreator = actionCreators[key];
      if (typeof actionCreator === 'function') {
        boundActionCreators[key] = bindActionCreator(actionCreator, dispatch);
      }
    }
    
    //返回 boundActionCreators
    return boundActionCreators;
  }

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions