Redux原理

Redux原理

Posted by SkioFox on December 18, 2018

Redux源码解读

Redux是出了名的短小精悍且没有任何依赖。所有的非核心逻辑都交给了中间件去处理,自己保持着很好的独立性。

  1. index.js是redux的入口函数
  import createStore from './createStore'
  import combineReducers from './combineReducers'
  import bindActionCreators from './bindActionCreators'
  import applyMiddleware from './applyMiddleware'
  import compose from './compose'
  import warning from './utils/warning'
  import __DO_NOT_USE__ActionTypes from './utils/actionTypes'

  function isCrushed() {}
  if (
    process.env.NODE_ENV !== 'production' &&
    typeof isCrushed.name === 'string' &&
    isCrushed.name !== 'isCrushed'
  ) {
    warning(
    )
  }

  export {
    createStore,
    combineReducers,
    bindActionCreators,
    applyMiddleware,
    compose,
    __DO_NOT_USE__ActionTypes
  }

isCrushed函数是用于验证在非生产环境下 Redux 是否被压缩,如果被压缩就会给开发者一个 warn 的提示。在最后index.js 会暴露 createStore, combineReducers, bindActionCreators, applyMiddleware, compose 这几个redux最主要的API以供大家使用。

  1. creatStore
    • createStore函数接受三个参数:
      • reducer:是一个函数,返回下一个状态,接受两个参数:当前状态和触发的action;
      • preloadedState:初始状态对象,可以很随意指定,比如服务端渲染的初始状态,但是如果使用combineReducers来生成reducer,那必须保持状态对象的key和combineReducers中的key相对应;
      • enhancer:是store的增强器函数,可以指定为中间件,持久化等,但是这个函数只能用Redux提供的applyMiddleware函数来进行生成
    import $$observable from 'symbol-observable'

    import ActionTypes from './utils/actionTypes'
    import isPlainObject from './utils/isPlainObject'

    export default function createStore(reducer, preloadedState, enhancer) {
      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 作为参数,对  createStore 的能力进行增强,并返回增强后的  createStore 。
        //  然后再将  reducer 和  preloadedState 作为参数传给增强后的  createStore ,最终得到生成的 store
        return enhancer(createStore)(reducer, preloadedState)
      }
      // reducer必须是函数
      if (typeof reducer !== 'function') {
        throw new Error('Expected the reducer to be a function.')
      }

    // 初始化参数
      let currentReducer = reducer   // 当前整个reducer
      let currentState = preloadedState   // 当前的state,也就是getState返回的值
      let currentListeners = []  // 当前的订阅store的监听器
      let nextListeners = currentListeners // 下一次的订阅
      let isDispatching = false // 是否处于 dispatch action 状态中, 默认为false

      // 这个函数用于确保currentListeners 和 nextListeners 是不同的引用
      function ensureCanMutateNextListeners() {
        if (nextListeners === currentListeners) {
          nextListeners = currentListeners.slice()
        }
      }

      // 返回state获取当前状态
      function getState() {
        if (isDispatching) {
          throw new Error(
            ......
          )
        }
        return currentState
      }

      // 添加订阅
      function subscribe(listener) {
          currentListeners.push(listener)
        }
      }
    // 分发action
      function dispatch(action) {
        currentState=reducer(currentState,action)
        currentListeners.forEach(v=>v())
        return action
      }

      //这个函数主要用于 reducer 的热替换,用的少
      function replaceReducer(nextReducer) {
        if (typeof nextReducer !== 'function') {
          throw new Error('Expected the nextReducer to be a function.')
        }
        // 替换reducer
        currentReducer = nextReducer
        // 重新进行初始化
        dispatch({ type: ActionTypes.REPLACE })
      }

      // 没有研究,暂且放着,它是不直接暴露给开发者的,提供了给其他一些像观察者模式库的交互操作。
      function observable() {
        ......
      }

      // 创建一个store时的默认state
      // 用于填充初始的状态树
      dispatch({ type: ActionTypes.INIT })

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

      function subscribe(listener) {
          if (typeof listener !== 'function') {
            throw new Error('Expected the listener to be a function.')
          }
    
          if (isDispatching) {
            throw new Error(
              ......
            )
          }
    
          let isSubscribed = true
          // 如果 nextListeners 和 currentListeners 是一个引用,重新复制一个新的
          ensureCanMutateNextListeners()
          nextListeners.push(listener)
    
          return function unsubscribe() {
            if (!isSubscribed) {
              return
            }
    
            if (isDispatching) {
              throw new Error(
                .......
              )
            }
              
            isSubscribed = false
            ensureCanMutateNextListeners()
            const index = nextListeners.indexOf(listener)
            // 从nextListeners里面删除,会在下次dispatch生效
            nextListeners.splice(index, 1)
          }
        }
    

    subscribe 函数可以给 store 的状态添加订阅监听,一旦我们调用了 dispatch 来分发 action ,所有的监听函数就会执行。而 nextListeners 就是储存当前监听函数的列表,当调用 subscribe,传入一个函数作为参数时,就会给 nextListeners 列表 push 这个函数。同时调用 subscribe 函数会返回一个 unsubscribe 函数,用来解绑当前传入的函数,同时在 subscribe 函数定义了一个 isSubscribed 标志变量来判断当前的订阅是否已经被解绑,解绑的操作就是从 nextListeners 列表中删除当前的监听函数。

  • dispatch

    dispatch是redux中一个非常核心的方法,也是我们在日常开发中最常用的方法之一。dispatch函数是用来触发状态改变的,他接受一个 action 对象作为参数,然后 reducer 就可以根据 action 的属性以及当前 store 的状态,来生成一个新的状态,从而改变 store 的状态

      function dispatch(action) {
        // action 必须是一个对象
        if (!isPlainObject(action)) {
          throw new Error(
            ......
          )
        }
        // type必须要有属性,不能是undefined
        if (typeof action.type === 'undefined') {
          throw new Error(
            ......
          )
        }
        // 禁止在reducers中进行dispatch,因为这样做可能导致分发死循环,同时也增加了数据流动的复杂度
        if (isDispatching) {
          throw new Error('Reducers may not dispatch actions.')
        }
    
        try {
          isDispatching = true
        // 将当前的状态和 action 传给当前的reducer,用于生成最新的 state
          currentState = currentReducer(currentState, action)
        } finally {  
          // 派发完毕
          isDispatching = false
        }
        // 将nextListeners交给listeners
        const listeners = (currentListeners = nextListeners)
        // 在得到新的状态后,依次调用所有的监听器,通知状态的变更
        for (let i = 0; i < listeners.length; i++) {
          const listener = listeners[i]
          listener()
        }
        return action
      }
    
      3. compose.js > 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)))
      }
    

    compose函数的作用其实其源码的注释里讲的很清楚了,比如下面这样:

      compose(funcA, funcB, funcC)
    

    其实它与这样是等价的:

      compose(funcA(funcB(funcC())))
    

    compose 做的只是让我们在写深度嵌套的函数时,避免了代码的向右偏移。变成加壳的链式调用。

  1. applyMiddleware

    applyMiddleware也是redux中非常重要的一个函数,用于扩展中间件。

    export default function applyMiddleware(...middlewares) {
      return createStore => (...args) => {
        // 利用传入的createStore和reducer和创建一个store
        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.`
          )
        }
        // 中间件的两个功能获取状态和dispatch
        const middlewareAPI = {
          getState: store.getState,
          dispatch: (...args) => dispatch(...args)
        }
        // 让每个 middleware 带着 middlewareAPI 这个参数分别执行一遍
        const chain = middlewares.map(middleware => middleware(middlewareAPI))
        // compose复合函数可以对chain进行包裹,一层一层往下一个中间件传递:将中间件变成链式调用 
        dispatch = compose(...chain)(store.dispatch)
        return {
          ...store,
          dispatch
        }
      }
    }

通过上面的代码,我们可以看出 applyMiddleware 是个三级柯里化的函数。它将陆续的获得三个参数:第一个是 middlewares 数组,第二个是 Redux 原生的 createStore,最后一个是 reducer,也就是上面的…args; applyMiddleware 利用 createStore 和 reducer 创建了一个 store,然后 store 的 getState 方法和 dispatch 方法又分别被直接和间接地赋值给 middlewareAPI 变量。

    // 核心
    dispatch = compose(...chain)(store.dispatch)

上面那段核心代码中,本质上就是这样的(假设…chain有三个函数):

    dispatch = f1(f2(f3(store.dispatch))))
  1. combineReducers

    ombineReducers 这个辅助函数的作用就是,将一个由多个不同 reducer 函数作为 value 的 object 合并成一个最终的 reducer 函数,然后我们就可以对这个 reducer 调用 createStore 方法了。 并且合并后的 reducer 可以调用各个子 reducer,并把它们返回的结果合并成一个 state 对象。 由 combineReducers() 返回的 state 对象,会将传入的每个 reducer 返回的 state 按其传递给 combineReducers() 时对应的 key 进行命名。

  export default function combineReducers(reducers) {
    const reducerKeys = Object.keys(reducers)
    // 有效的 reducer 列表
    const finalReducers = {}
    for (let i = 0; i < reducerKeys.length; i++) {
      const key = reducerKeys[i]
    const finalReducerKeys = Object.keys(finalReducers)

  // 返回最终生成的 reducer
    return function combination(state = {}, action) {
      let hasChanged = false
      //定义新的nextState
      const nextState = {}
      // 1,遍历reducers对象中的有效key,
      // 2,执行该key对应的value函数,即子reducer函数,并得到对应的state对象
      // 3,将新的子state挂到新的nextState对象上,而key不变
      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
      }
      // 遍历一遍看是否发生改变,发生改变了返回新的state,否则返回原先的state
      return hasChanged ? nextState : state
    }
  }
  1. bindActionCreators

    bindActionCreators可以把一个 value 为不同 action creator 的对象,转成拥有同名 key 的对象。同时使用 dispatch 对每个 action creator 进行包装,以便可以直接调用它们。

      // 核心代码,并通过apply将this绑定起来
      function bindActionCreator(actionCreator, dispatch) {
     return function() {
       return dispatch(actionCreator.apply(this, arguments))
     }
      } 
      // 这个函数只是把actionCreators这个对象里面包含的每一个actionCreator按照原来的key的方式全部都封装了一遍,核心代码还是上面的
      export default function bindActionCreators(actionCreators, dispatch) {
     // 如果actionCreators是一个函数,则说明只有一个actionCreator,就直接调用bindActionCreator
     if (typeof actionCreators === 'function') {
       return bindActionCreator(actionCreators, dispatch)
     }
     // 如果是actionCreator是对象或者null的话,就会报错
     if (typeof actionCreators !== 'object' || actionCreators === null) {
       throw new Error(
       ... ... 
     }
      // 遍历对象,然后对每个遍历项的 actionCreator 生成函数,将函数按照原来的 key 值放到一个对象中,最后返回这个对象
     const keys = Object.keys(actionCreators)
     const boundActionCreators = {}
     for (let i = 0; i < keys.length; i++) {
       const key = keys[i]
       const actionCreator = actionCreators[key]
       if (typeof actionCreator === 'function') {
         boundActionCreators[key] = bindActionCreator(actionCreator, dispatch)
       }
     }
     return boundActionCreators
      }