前言

学习源码,个人认为最佳的方式是带着问题去看源码,那么对于redux我们会有什么想要知道的问题呢?

  1. createStore做了什么?
  2. dispatch做了什么?
  3. reducer做了什么?
  4. redux中间件怎么实现?
  5. redux的state树怎么和react交互?

源码分析

这是redux源码的文件目录,一个文件一个文件看,让我们带着问题去看一下源码的实现方式。

createStore

import isPlainObject from 'lodash/isPlainObject'
import $$observable from 'symbol-observable'

//redux自己创建的一个action,最后面有dispatch调用,用来初始化状态树和reducer改变后初始化状态树
export const ActionTypes = {
  INIT: '@@redux/INIT'
}

export default function createStore(reducer, preloadedState, enhancer) {
  //如果第二个参数为方法且第三个参数为空,则将两个参数交换. 平时使用
  //createStore(reducer, applyMiddleware(...))就会调用到这里
  if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
    enhancer = preloadedState
    preloadedState = undefined
  }

  //enhancer和reducer必须为function类型
  if (typeof enhancer !== 'undefined') {
    if (typeof enhancer !== 'function') {
      throw new Error('Expected the enhancer to be a function.')
    }
    //将enhancer包装一次createStore方法,再调用无enhancer的createStore方法
    return enhancer(createStore)(reducer, preloadedState)
  }

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

  let currentReducer = reducer //当前的reducer函数
  let currentState = preloadedState //当前的state树
  let currentListeners = []      //监听函数列表
  let nextListeners = currentListeners  //监听列表的一个引用
  let isDispatching = false      //是否正在dispatch

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

  //下面的函数被return出去,函数作为返回值,则形成了闭包,currentState等状态会被保存
  
  //返回当前state树
  function getState() {
       //...
  }

  //添加注册一个监听函数,返回一个可以取消此监听的方法
  function subscribe(listener) {
       //...
  }

  function dispatch(action) {
      //...
  }

  //替换当前reducer
  function replaceReducer(nextReducer) {
      //...
  }

  function observable() {
      //...
  }

  // 当store被创建的时候,初始化状态树
  dispatch({ type: ActionTypes.INIT })

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

由此文件我们便可以知道第一个问题,createStore的时候,redux做了什么。观察函数我们可以发现,createStore()接受三个参数:

  • reducer 参数是一个函数,用于返回下一个state
  • preloadedState 可选项。preloadedState可以设置初始的state
  • enhancer store增强器,只能使用'applyMiddleware'方法来生成

然后返回一个对象,里面包含了state树和一些方法,这就是createStore做的事情,下面我们一个一个函数的进行分析:

dispatch

function dispatch(action) {
    //两个if,一个判断action是否为对象,一个判断action对象是否含type
  	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?'
      )
    }

    //如果正处于isDispatching状态,报错
    if (isDispatching) {
      throw new Error('Reducers may not dispatch actions.')
    }

    try {
      isDispatching = true
      //这里就是调用我们reducer方法的地方,返回一个新的state作为currentState
      currentState = currentReducer(currentState, action)
    } finally {
      isDispatching = false
    }

    //调用所有的监听函数
    const listeners = currentListeners = nextListeners
    for (let i = 0; i < listeners.length; i++) {
      const listener = listeners[i]
      listener()
    }

    return action
  }

看到这个函数就能解决我们的第二个问题了,dispatch做了什么呢?首先,dispatch会把当前的reducerstate两个参数传到当前的currentReducer函数当中,从而获取新的state作为currentState。然后再调用所有的监听函数。看到这里我们又有新的问题了,监听函数是干什么的呢 ?我们接着往下看。

subscribe

function subscribe(listener) {
    if (typeof listener !== 'function') {
      throw new Error('Expected listener to be a function.')
    }

    let isSubscribed = true
	// 这个函数在前面,作用如函数名,确定能添加这个新的listener到nextListeners
    ensureCanMutateNextListeners()
    nextListeners.push(listener)

    return function unsubscribe() {
      if (!isSubscribed) {
        return
      }

      isSubscribed = false

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

简单来说这个函数用于添加监听函数,把需要监听的函数放到nextListeners里面,再返回一个可以取消订阅的方法。那么问题来了,哪些函数需要加进这个数组里面呢?换句话说,什么时候执行这个订阅函数?我们接着往下看.

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
  }

顾名思义,就是放回当前的state

replaceReducer

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

    currentReducer = nextReducer
    dispatch({ type: ActionTypes.INIT })
 }

替换当前的reducer,替换后重新初始化state树.

observable

function observable() {
    //获取订阅函数,使outerSubscribe有subscribe的功能
    const outerSubscribe = subscribe
    return {
      subscribe(observer) {
        if (typeof observer !== 'object') {
          throw new TypeError('Expected the observer to be an object.')
        }

        function observeState() {
          //观察者模式的链式结构,为观察者传入当前的state
          if (observer.next) {
            observer.next(getState())
          }
        }

        observeState()
        //取消订阅函数
        const unsubscribe = outerSubscribe(observeState)
        return { unsubscribe }
      },

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

观察者模式用到的函数,其实从这里大概就能猜到subscribe函数以及nextListeners的作用了,这些都与订阅/观察者模式有关,主要作用是在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)))
}

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))).

官方解释,意思就是该函数用于传入一个函数数组,返回一个参数为args数组的该函数数组函数的链式调用。注意reduce的使用方法即可.

combindReducer

import { ActionTypes } from './createStore'
import isPlainObject from 'lodash/isPlainObject'
import warning from './utils/warning'

//根据key和action生成错误信息
function getUndefinedStateErrorMessage(key, action) {
  //...
}

//一些state不符合要求的错误警告
function getUnexpectedStateShapeWarningMessage(inputState, reducers, action, unexpectedKeyCache) {
  //...
}


//这个方法用于检测用于组合的reducer是否是符合redux规定的reducer
function assertReducerSanity(reducers) {
  //...
}

export default function combineReducers(reducers) {
  const reducerKeys = Object.keys(reducers) //所有的键名
  const 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}"`)
      }
    }

    //finalReducers是过滤后的reducers,它的每一个属性都是一个function
    if (typeof reducers[key] === 'function') {
      finalReducers[key] = reducers[key]
    }
  }

  const finalReducerKeys = Object.keys(finalReducers)

  let unexpectedKeyCache
  if (process.env.NODE_ENV !== 'production') {
    unexpectedKeyCache = {}
  }

  let sanityError

  //检测每个reducer是否是符合标准的reducer
  try {
    assertReducerSanity(finalReducers)
  } catch (e) {
    sanityError = e
  }
  // combindReducer最终返回的函数,参数于一个reducer相同,为当前的state,和action
  return function combination(state = {}, action) {
    if (sanityError) {
      throw sanityError
    }

    //如果不是成产环境,做一些警告判断
    if (process.env.NODE_ENV !== 'production') {
      const warningMessage = getUnexpectedStateShapeWarningMessage(state, finalReducers, action, unexpectedKeyCache)
      if (warningMessage) {
        warning(warningMessage)
      }
    }

    let hasChanged = false
    const nextState = {} //下一个state树

    //遍历所有reducers,然后将每个reducer返回的state组合起来生成一个大的状态树,所以任何action,redux都会遍历所有的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)

      //如果此reducer返回的新的state是undefined,抛出异常
      if (typeof nextStateForKey === 'undefined') {
        const errorMessage = getUndefinedStateErrorMessage(key, action)
        throw new Error(errorMessage)
      }
      nextState[key] = nextStateForKey
      // nextStateForKey和previousStateForKey的比较是对象比较,所以我们每次都要返回一个新的对象才能触发组件重新渲染
      hasChanged = hasChanged || nextStateForKey !== previousStateForKey
    }
    //如果当前action对应的reducer方法执行完后,该处数据没有变化,则返回原来的流程树
    return hasChanged ? nextState : state
  }
}

combindReducer的作用就是把所有的reducer组合起来成为一个新的reducer,参数与单独一个reducer的时候相同,都是当前的state和action。我们可以从返回的函数combination(state = {}, action),再结合dispatch函数的这一段代码:

function dispatch(action) {
  //...
  currentState = currentReducer(currentState, action)
  //...
}

就能知道,第三个问题的答案,reducer的作用就是在触发action之后,返回一个新的state状态树.而combindReducer的作用就是把所有reducer组合成一个,在任一actiondispatch的时候,调用所有的reducer,并根据reducer的键值组成一个新对象,返回一个新的state状态大树.所以我们从connect拿出来的时候,是state.xxx(reducer定义的名称).xxxx(具体数据)这样的样式。

bindActionCreators

import warning from './utils/warning'

// 执行函数,生成已dispatch了action的函数
function bindActionCreator(actionCreator, dispatch) {
  return (...args) => dispatch(actionCreator(...args))
}

export default function bindActionCreators(actionCreators, dispatch) {
  if (typeof actionCreators === 'function') {
    return bindActionCreator(actionCreators, dispatch)
  }
    
  //actionCreators必须为object类型
  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"?`
    )
  }

  const keys = Object.keys(actionCreators)
  const boundActionCreators = {}
  for (let i = 0; i < keys.length; i++) {
    const key = keys[i]
    const actionCreator = actionCreators[key]
    
    //给actionCreators的每一个成员都绑定dispatch方法生成新的方法,
    //然后注入新的对象中,新方法对应的key即为原来在actionCreators的名字
    if (typeof actionCreator === 'function') {
      boundActionCreators[key] = bindActionCreator(actionCreator, dispatch)
    } else {
      warning(`bindActionCreators expected a function actionCreator for key '${key}', instead received type '${typeof actionCreator}'.`)
    }
  }
  return boundActionCreators
}

把所有action转换成不用dispatch的action函数,具体解释可以看注释。

applyMiddleware

export default function applyMiddleware(...middlewares) {

  //return一个函数,它可以接受createStore方法作为参数的函数,该函数的主要作用是
  //提取出createStore过程中返回的dispatch函数对它进行一次包装
  return (createStore) => (reducer, preloadedState, enhancer) => {
    const store = createStore(reducer, preloadedState, enhancer)
    let dispatch = store.dispatch
    let chain = []

    //作为外部函数的参数
    const middlewareAPI = {
      getState: store.getState,
      dispatch: (action) => dispatch(action)
    }

    //传入middlewareAPI参数并执行每一个外部函数,返回结果汇聚成数组
    chain = middlewares.map(middleware => middleware(middlewareAPI))

    //重新包装dispatch函数,包装成一个链式调用函数
    dispatch = compose(...chain)(store.dispatch)
	
    //对象解构,下面的dispatch会覆盖解构出来的dispatch
    return {
      ...store,
      dispatch
    }
  }
}

专用于createStoreenhancer参数。首先要了解这个函数的作用,我们回头看一下dispatch的实现,会发现dispatch只接受action为对象,从上面的注释也可以看出来applyMiddleware最大的作用就是重新包装dispatch函数,例如redux-thunk,redux-promise等,能让dispatch函数接受action为函数或者promise。我们再通过redux-thunk来看看具体的实现,下面是redux-thunk的源代码:

function createThunkMiddleware(extraArgument) {
  return ({ dispatch, getState }) => next => action => {
    if (typeof action === 'function') {
      return action(dispatch, getState, extraArgument);
    }

    return next(action);
  };
}

const thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware;

export default thunk;

调用redux-thunk,即是applyMiddleware(thunk),thunk是一个返回以{ dispatch, getState}为参数的函数的函数,回头再看applyMiddleware的代码,当只有redux-thunk一个参数的时候,即chain为执行这个函数后返回结果(一个以next为参数的函数)的数组,再来看看chain的下一行,是compose函数,根据之前分析可知compose返回一个函数链式调用的返回值,现在只有一个函数,所以返回值就是把store.dispatch做为参数next的函数,此时dispatch就变成以下结果:

dispatch = (store.dispatch) => action => {
    if (typeof action === 'function') {
      //这里作为参数的dispatch是原来的dispatch
      return action(dispatch, getState, extraArgument);
    }
    // 如果不是函数,则直接返回原来的dispatch
    return store.dispatch(action);
}

意思即是原来的dispatch(action),变成了如果传进来的action是function的话,就执行该函数,把原来dispatch作为参数传进去,函数执行到dispatch的时候,再执行原来的dispatch。从而让action可以是一个包含dispatch的函数而不单单只能是对象。

总结

到这里源代码的分析就结束了,总的来说还是挺有意思的,特别是中间件实现的写法,函数式编程接触的少,咋一看有点新奇,一开始提出的问题解决了4个,最后一个问题就与react-redux相关,有机会再聊吧!

参考

  1. https://segmentfault.com/q/1010000012332458
  2. https://www.jianshu.com/p/728dbb1de25e