7

Redux API 和 Redux 源码结构

Redux API

export {
    createStore,             //创建一个state用来存储状态树
    combineReducers,   //合并reducer
    bindActionCreators,  //将dispatch和action结合
    applyMiddleware,     //调度中间件来增强store,例如中间件redux-thunk等
    compose              //从右向左组合多个函数, compose(f, g, h)会返回(...args) => f(g(h(...args)))
}

源码结构

Redux源码结构和提供的API大体对应,如下:
utils——warning.js //console中打印warning信息要用到的
applyMiddleware.js
bindActionCreators.js
combineReducers.js
compose.js
createStore.js
index.js //export 上述定义的module

createStore

上面我们看到了redux的API和源码结构,看的出来,warning.js和index.js不用解析,都看得懂,关键时其余的几个module,那我们从最重要的createStore讲起。

    export var ActionTypes = {
        INIT: '@@redux/INIT'
    }
   //首先定义了一个action类型,我们知道更新state的唯一方法就是dispatch一个action,这个action是用
   // 来初始化state的,后面会用到它

现在来看下createStore的大体结构

//接收三个参数
//reducer为function。当dispatch一个action时,此函数接收action来更新state
//preloadState初始化State
//enhancer 为function。用来增强store, Redux 定义有applyMiddleware来增强store,后面会
//单独讲applyMiddleware

export default function createStore(reducer, preloadedState, enhancer) {
    if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
        //如果只传了两个参数,并且第二个参数为函数,第二个参数会被当作enhancer
        enhancer = preloadedState
        preloadedState = undefined
    }
  
    if (typeof enhancer !== 'undefined') {
        if (typeof enhancer !== 'function') {
            //校验enhancer是否为函数,如果不是函数则抛出异常
            throw new Error('Expected the enhancer to be a function.')
        }
        //如果enhancer存在且为函数,那么则返回如下调用,如果enhancer为applyMiddleware,那么调用则
        //是applyMiddleware(middleware1, middleware2, middleware3...)(createStore)(reducer, preloadedState)。后面讲applyMiddleware再详细讲。
        return enhancer(createStore)(reducer, preloadedState)
    }

    if (typeof reducer !== 'function') {
        //校验reducer是否为函数
        throw new Error('Expected the reducer to be a function.')
    }

    var currentReducer = reducer
    //得到reducer

    var currentState = preloadedState
    //得到初始init,没有传递则为undefined

    var currentListeners = []
    //定义一个数组用来存放listeners。就是一个函数数组,当state发生改变时,会循环执行这个数组里面的函数

    var nextListeners = currentListeners
    //用来存储下一次的listeners数组。为什么要有这个listeners数组呢?因为当state发生改变时,我们根据
    //上面的currentListeners来循环执行函数,但是在这执行这些函数时,函数内部可能取消或者添加订阅
    //(state改变时,添加或者取消执行函数),这时如果直接操作currentListeners ,相当于在循环
    //内部修改循环条件,执行瞬间就乱套了,有没有啊,有没有

    var isDispatching = false
    //reducer函数是否正在执行的标识

    function ensureCanMutateNextListeners() {
        //拷贝currentListeners一份为nextListeners,这样nextListeners的改变不会引起currentListeners的改变
        //(上面解释过原因啦)
    }

    function dispatch() {
        //触发action去执行reducer,更新state
        .....
    }

    function subscribe() {
        //接收一个函数参数,订阅state的改变。当state改变时会执行这个函数
        ....
    }

    function getState() {
        //获取state树
        ....
    }

    function replaceReducer() {
        //替换reducer
        ....
    }

    function observable() {
        //没用,不解释(后面有解释)
        ......
    }


    dispatch({ type: ActionTypes.INIT })
    //执行dispatch函数,初始化state

    return {
        //真正的返回,执行createStore其实返回的就是这些东东
        dispatch,       //触发action去执行reducer,更新state
        subscribe,     //订阅state改变,state改变时会执行subscribe的参数(自己定义的一个函数)
        getState,      //获取state树
        replaceReducer,       //替换reducer
        [$$observable]: observable         
        //redux内部用的,对我们来说没用(非要深究它写这是干嘛的?咋跟
        //我一样死脑筋呢,都说了没用啦。算了,还是告诉你把,就是内部用的,在测试代码中会用到,感兴
        //趣的可以去test目录下查看)
    }
}

现在是不是感觉明朗(懵逼)了许多,懵逼就对了,接下来我们再来解析一下dispatch, subscribe等函数的具体实现,或许会让你明朗(更懵逼)起来
看了上面的大体结构,我们明白以下这些就够了。
createStore是一个函数,它定义了一些变量(currentState, currentListeners等)及函数(dispatch, subscribe等),并且调用了dispatch,最后返回一个对象,该对象包含的就是dispatch和subscribe等函数。接下来我们来解析这些函数。
createStore里面只调用了一个函数,那就是dispatch,那我们就从这里开始讲起。

dispatch

它是这样被调用的,有没有很熟悉。。。

dispatch({ type: ActionTypes.INIT })

来看看dispatch的源码

  function dispatch(action) {
    //校验action参数,必须为一个纯粹的对象。这也说明了,我们不能直接在redux做异步请求,而是需要
    //使用applyMiddleware去应用一些中间件。比如redux-thunk等
    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?'
      )
    }
    //判断是否正在执行reducers函数,如果正在执行,此action不会触发,那有人就问题了,那是不是我
    //的dispatch不会起作用,导致state没有更新,数据是错误的? 答案是state是不会有错。因为redux本
    //身整个更新state的过程是同步的,从dipatch——>reducers——>state。所以这段代码意在你定义的
    //reducers函数中不允许调用dispatch
    if (isDispatching) {
      throw new Error('Reducers may not dispatch actions.')
    }

    try {
      //设置标识为true,并且执行currentReducer,还记得吗?这个我们通过参数获取到的一个函数,往往是我
      //们调用combineReducers返回,combineReducers我们后面解析
      isDispatching = true
      //调用reducers函数
      currentState = currentReducer(currentState, action)
    } finally {
      isDispatching = false
    }
    //调用所有订阅state改变的函数,这些函数就可以通过getState函数获取到最新的state值。订阅的函数
    //从哪里来呢,从subscribe中来, 我们后面来解析subscribe和getState
    var listeners = currentListeners = nextListeners
    for (var i = 0; i < listeners.length; i++) {
      listeners[i]()
    }

    return action
  }

NsNe
1.7k 声望38 粉丝

善良,学习,拼搏。不忘初心,方得始终。