Redux源码学习

312 阅读6分钟

1.createStore

createStore是使用redux的入口,也是使用redux必须用到的一个方法,其顾名思义就是创建一个可供全局使用的store

该方法接受三个参数(reducer, preloadedState, enhance)

前两个参数不必多说,第三个参数则是用来添加中间件,他是以原始的createStore为参数,而createStore真正需要的数据为reducerpreloadedState

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

行代码展示了enhancer的调用过程,根据这个调用过程我们可以推导出enhancer的函数体的架子应该是这样子的:

 function enhancer(createStore) {
    return (reducer,preloadedState) => {
         //逻辑代码
        .......
    }
 }
在实际应用中,enhancer往往就是redux-thunk以及redux-saga,一般都会配合applyMiddleware一起使用,而applyMiddleware的作用就是将这些enhancer格式化成符合redux要求的enhancer。

createStore返回的是一个对象,我们通常称之为store,store的结构如下:

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

createStore的返回值为一个对象,我们常用的为前三个方法即dispatch, subscribe, getState这三种方法即服务于将数据流以flux的形式进行传输,即在对store的change进行监听,并以订阅发布模式执行每个监听函数,当然redux在每个阶段都做了异常检测。

dispatchaction的做了isPlainObjectundefined的判断,并在dispatching的时候用一个flag–isDispatching来标志状态,保证在同一时段只有一个dispatch

subscribe接受listener为参数,此处的listener是什么?该函数首先确定listenerfunction ,然后对isDispatching做了判断,确保store 的稳定性,然后以一个flag -- isSubscribed 来标志该listener 的监听状态,同时对currentListeners 做了次浅拷贝后将 listener 添加到监听队列里,此时完成了对监听事件的绑定,之后返回一个该listener取消订阅的函数,该函数里面即把 isSubscribed置位,并又对currentListeners做了次浅拷贝后,将 listener 从监听队列里移除。

这里每次在改变监听队列前都对监听队列做了一次浅拷贝的原因是在于防止当redux在通知所有订阅者的时候,此时又有一个新的订阅者加进来了。如果只用 currentListeners 的话,当新的订阅者插进来的时候,就会打乱原有的顺序,从而引发一些严重的问题。

getState是redux暴露store的唯一方法,然后该方法内部也只对 isDispatching 做了判断,避免在 store 改变时被获取,然后直接返回store,但是不能直接对state赋值,而是通过setState的方法改变state,用来触发订阅者的监听函数。

2.combineReducers

combineReducers函数来把多个reducer 函数合并成一个reducer函数。

其参数为一个json对象及所有reducer的一个集合,key为store该reducer对应的属性,值为对应的reducer(一个函数)

返回值为一个合并后的reducer函数

这个函数做了三件事:

1.对参数和参数的key(对应store中的属性)做了一个浅拷贝

2.对浅拷贝后的参数中每个reducer做返回值检测(检测是否有默认返回值)

3.返回一个合并后的reducer函数,而在返回的函数内部,combineReducers对其也做了异常检测。异常检测主要是由 getUnexpectedStateShapeWarningMessage 该方法执行,

getUnexpectedStateShapeWarningMessage接收四个参数 inputState(state)reducers(finalReducers)、action(action)unexpectedKeyCache(unexpectedKeyCache),这里要说一下unexpectedKeyCache是上一次检测inputState得到的其里面没有对应的reducer集合里的异常key的集合。整个逻辑如下:

  1. 前置条件判断,保证reducers集合不为{}以及inputState为简单对象
  2. 找出inputState里有的key但是 reducers集合里没有key
  3. 如果是替换reducer的action,跳过第四步,不打印异常信息
  4. 将所有异常的key打印出来

再异常检测定义了一个hasChanged变量用来表示state是否发生变化,遍历reducers集合,将每个reducer对应的原state传入其中,得出其对应的新的state。

3.applyMiddleware和compose

上面讲enhance的时候说过,applyMiddleware 返回的就是一个enhanceapplyMiddleware 顾名思义就是一个添加中间件的方法,而中间件添加的最佳时机在于dispatch ,因为 dispatch就是store 改变的发起动作。

中间件很可能不止一个,因此写成链式结构,可以将其解耦,而第一个传入的参数则为dispatch。---compose

下面是一个 applyMiddleware 的逻辑实现。

const applyMiddleware = function (...middlewares) { /*返回一个重写createStore的方法*/ return function rewriteCreateStoreFunc(oldCreateStore) { /*返回重写后新的 createStore*/ return function newCreateStore(reducer, initState) { /*1. 生成store*/ const store = oldCreateStore(reducer, initState); /*给每个 middleware 传下store,相当于 const logger = loggerMiddleware(store);*/ /* const chain = [exception, time, logger]*/ const chain = middlewares.map(middleware => middleware(store)); let dispatch = store.dispatch; /* 实现 exception(time((logger(dispatch))))*/ chain.reverse().map(middleware => { dispatch = middleware(dispatch); });

/2. 重写 dispatch/ store.dispatch = dispatch; return store; } } }

applyMiddleware主要就是通过链式加强dispatch。实际的js代码如下:

export default function applyMiddleware(...middlewares) {
  return createStore => (...args) => {
    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)
    }
    const chain = middlewares.map(middleware => middleware(middlewareAPI))
    dispatch = compose(...chain)(store.dispatch)

    return {
      ...store,
      dispatch
    }
  }
}

总体的实现逻辑为:

  1. 通过createStore方法创建出一个store
  2. 定一个dispatch,如果在中间件构造过程中调用,抛出错误提示。即防止在中间件构造过程中调用dispatch
  3. 定义middlewareAPI,有两个方法,一个是getState,另一个是dispatch,将其作为中间件调用的store的桥接
  4. middlewares调用Array.prototype.map进行改造,存放在chain
  5. 用compose整合chain数组,并赋值给dispatch
  6. 将新的dispatch替换原先的store.dispatch

compose的作用即将所有中间件以链式的形式调用。

4.bindActionCreators

bindActionCreators 很少用到,一般只有在 react-redux 的 connect 函数中的mapDispatchToProps中用到。

其作用网上说是他通过闭包,把 dispatchactionCreator 隐藏起来,让其他地方感知不到 redux 的存在。但是我还是不知道这样做的意义是在于?

bindActionCreators内部针对于三种情况有三种返回值。

export default function bindActionCreators(actionCreators, dispatch) {
  if (typeof actionCreators === 'function') {
    return bindActionCreator(actionCreators, dispatch)
  }

  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]
    if (typeof actionCreator === 'function') {
      boundActionCreators[key] = bindActionCreator(actionCreator, dispatch)
    }
  }
  return boundActionCreators
}

第一种情况,当 typeof actionCreators === 'function' 时,

直接返回

return function() {
    return dispatch(actionCreator.apply(this, arguments))
}

即直接返回一个返回值为 dispatch某个action 的函数的函数。

第二种情况,当typeof actionCreators !== 'object' || actionCreators === null

一直抛出一个 Error ,指出bindActionCreators 需要的 actionCreator 是一个函数或者对象。

第三种情况,即默认情况,这个时候的 actionCreator 是很多个第一种情况的集合,即 import * as manyActions from '../actions',这里的 manyActions 就是作为第三种情况传入 boundActionCreatorsactionCreators

这个时候会对actionCreators 做一个遍历,将每一项都执行一遍第一种情况的操作。