在之前的章节当中我们介绍了 Flux 与 Redux 和 Redux、Flux 和 React-Redux 三者之间的区别 ,这一章我们就深入的来了解一下 Redux
的源码结构,先来看看 Redux
的源码目录,如下所示
1 2 3 4 5 6 7 8 ├── utils/ │ ├── warning.js ├── applyMiddleware.js ├── bindActionCreators.js ├── combineReducers.js ├── compose.js ├── createStore.js ├── index.js
可以发现,除开 index
和 warning
以外,剩余的五个就是 Redux
的 API
,下面我们就一个一个来看
compose(…functions) compose()
方法没有任何依赖,是一个纯函数,它的使用方式是
1 compose(f, g, h)(...arg) => f(g(h(...args)))
不过值得注意的是,它用到了 reduceRight
,因此执行顺序是『从右到左』,reduceRight()
方法的功能和 reduce()
功能是一样的,不同的是 reduceRight()
从数组的末尾向前将数组中的数组项做累加,具体实现方式如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 export default function compose (...funcs ) { if (funcs.length === 0 ) { return arg => arg } if (funcs.length === 1 ) { return funcs[0 ] } const last = funcs[funcs.length - 1 ] const rest = funcs.slice(0 , -1 ) return (...args ) => rest.reduceRight((composed, f ) => f(composed), last(...args)) }
这里的关键点在于『可传入初始值』,因为 reduce/reduceRight
仅仅是方向的不同
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 var arr = [1 , 2 , 3 , 4 , 5 ]var re1 = arr.reduce(function (total, i ) { return total + i }) console .log(re1) var re2 = arr.reduce(function (total, i ) { return total + i }, 100 ) console .log(re2)
一个比较完整的示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 function func1 (num ) { console .log('func1 获得参数 ' + num) return num + 1 } function func2 (num ) { console .log('func2 获得参数 ' + num) return num + 2 } function func3 (num ) { console .log('func3 获得参数 ' + num) return num + 3 } var re1 = func3(func2(func1(0 )))console .log('re1:' + re1)console .log('===============' )var re2 = Redux.compose(func3, func2, func1)(0 )console .log('re2:' + re2)
combineReducers(reducers) 简单来说,这个函数的作用就是通过逐层下分管理对应部分的 state
(拆分 state
,各个模块管理自己的 state
,最后合并),因为在 Flux
中是根据不同的功能拆分出多个 store
分而治之,而 Redux
只允许应用中有唯一的 store
,通过拆分出多个 reducer
分别管理对应的 state
,无论是 dispatch
哪个 action
,都会流通所有的 reducer
,这也是为何 reducer
必须返回其对应的 state
的原因(否则整合状态树时,该 reducer
对应的键值就是 undefined
)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 function combineReducers (reducers ) { var reducerKeys = Object .keys(reducers) var finalReducers = {} for (var i = 0 ; i < reducerKeys.length; i++) { var key = reducerKeys[i] if (typeof reducers[key] === 'function' ) { finalReducers[key] = reducers[key] } } var finalReducerKeys = Object .keys(finalReducers) return function combination (state = {}, action ) { var hasChanged = false var nextState = {} for (var i = 0 ; i < finalReducerKeys.length; i++) { var key = finalReducerKeys[i] var reducer = finalReducers[key] var previousStateForKey = state[key] var nextStateForKey = reducer(previousStateForKey, action) nextState[key] = nextStateForKey hasChanged = hasChanged || nextStateForKey !== previousStateForKey } return hasChanged ? nextState : state } }
bindActionCreators(actionCreators, dispatch) 这个函数主要用于分发 action
(比如使用 dispatch(actionCreator())
),实现自动 dispatch
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 function bindActionCreator (actionCreator, dispatch ) { return (...args ) => dispatch(actionCreator(...args)) } export default function bindActionCreators (actionCreators, dispatch ) { var keys = Object .keys(actionCreators) var boundActionCreators = {} 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) } } return boundActionCreators }
createStore() 源码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 import isPlainObject from 'lodash/isPlainObject' import $$observable from 'symbol-observable' export var ActionTypes = { INIT: '@@redux/INIT' } export default function createStore (reducer, preloadedState, enhancer ) { var currentReducer = reducer var currentState = preloadedState var currentListeners = [] var nextListeners = currentListeners var isDispatching = false function ensureCanMutateNextListeners ( ) { if (nextListeners === currentListeners) { nextListeners = currentListeners.slice() } } function getState ( ) { return currentState } function subscribe (listener ) { if (typeof listener !== 'function' ) { throw new Error ('Expected listener to be a function.' ) } var isSubscribed = true ensureCanMutateNextListeners() nextListeners.push(listener) return function unsubscribe ( ) { if (!isSubscribed) { return } isSubscribed = false ensureCanMutateNextListeners() var index = nextListeners.indexOf(listener) nextListeners.splice(index, 1 ) } } 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) } finally { isDispatching = false } var listeners = currentListeners = nextListeners for (var i = 0 ; i < listeners.length; i++) { listeners[i]() } return action } function replaceReducer (nextReducer ) { if (typeof nextReducer !== 'function' ) { throw new Error ('Expected the nextReducer to be a function.' ) } currentReducer = nextReducer dispatch({ type : ActionTypes.INIT }) } function observable ( ) { 略 } dispatch({ type : ActionTypes.INIT }) return { dispatch, subscribe, getState, replaceReducer, [$$observable]: observable } }
反转控制权 所谓的反转控制权即
在同步场景下,dispatch(action)
的这个 action
中的数据是同步获取的,并没有控制权的切换问题
但异步场景下,则需要将 dispatch
传入到回调函数,待异步操作完成后,回调函数自行调用 dispatch(action)
简单来说就是,在异步 Action Creator
中自行调用 dispatch
就相当于反转控制权
它们的作用也仅仅就是把 dispatch
等传入异步 Action Creator
罢了
applyMiddleware(…middlewares) 我们先来简单的看一眼源码,内容不是很多,我们下面会慢慢来进行介绍
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 function applyMiddleware (...middlewares ) { 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) } chain = middlewares.map(middleware => middleware(middlewareAPI)) dispatch = compose(...chain)(store.dispatch) return { ...store, dispatch } } }
我们注意到 applyMiddleware
作为 enhancer
又把 createStore
这个函数作为参数传入并在内部返回函数中调用了,这其实也是依赖注入的理念,然后我们可以发现内部其实将 applyMiddleware
的入参传入的中间件都执行了一次,传参为 getState
和 dispatch
,这里先打住,我们先来看一个中间件的示例,就是一个打印动作前后 state
的中间件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 function printStateMiddleware (middlewareAPI ) { return function (dispatch ) { return function (action ) { console .log('state before dispatch' , middlewareAPI.getState()) var returnValue = dispatch(action) console .log('state after dispatch' , middlewareAPI.getState()) return returnValue } } } const printStateMiddleware = ({ getState } ) => next => action => { console .log('state before dispatch' , getState()) let returnValue = next(action) console .log('state after dispatch' , getState()) return returnValue }
通过上面代码我们可以发现,一般 middleWare
的内部构造都遵从一个 ({ getState, dispatch }) => next => action => {...}
的范式,并且导出的时候已经被调用了一次,即返回了一个需要接收 getState
和 dispatch
的函数
了解了这一点以后,我们再往后看,接着通过 compose
将中间件高阶组合并增强传入原 store.dispatch
的功能,最后再在返回值内解构覆盖原始 store
的 dispatch
,所以这个时候,我们应该已经可以了解 applyMiddleware
到底做了什么,那就是『增强了原始 createStore
返回的 dispatch
的功能』
下面我们就来详细的梳理一下 applyMiddleware
的实现,如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 export default function applyMiddleware (...middlewares ) { return function (createStore ) { return function (reducer, preloadedState, enhancer ) { var store = createStore(reducer, preloadedState, enhancer) var dispatch = store.dispatch var chain = [] var middlewareAPI = { getState: store.getState, dispatch: (action ) => dispatch(action) } chain = middlewares.map(middleware => middleware(middlewareAPI)) dispatch = compose(...chain)(store.dispatch) return { ...store, dispatch } } } }
关于上面代码当中的 chain
这里我们多提及一点,例如 chain
为 [M3, M2, M1]
,而 compose
是从右到左进行包裹的,所以
M1
的 dispatch
参数为 store.dispatch
M2
的 dispatch
参数为 M1
的 (2)
M3
的 dispatch
参数为 M2
的 (2)
最后,我们得到串联后的中间件链 M3(M2(M1(store.dispatch)))
,这也就是所谓的『中间件的洋葱模型』
以上,经过我们的梳理以后也可以大致了解到,其实最终返回的虽然还是 store
当中的那四个 API
,但是其中的 dispatch
函数的功能是已经被增强了的,也就是下图当中所示『中间件提供的是位于 action
被发起之后,到达 reducer
之前的扩展点』
一个综合案例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 function inc ( ) { return { type : 'INCREMENT' } } function dec ( ) { return { type : 'DECREMENT' } } function reducer (state, action ) { state = state || { counter : 0 } switch (action.type) { case 'INCREMENT' : return { counter : state.counter + 1 } case 'DECREMENT' : return { counter : state.counter - 1 } default : return state } } function printStateMiddleware (middlewareAPI ) { return function (dispatch ) { return function (action ) { console .log('dispatch 前:' , middlewareAPI.getState()) var returnValue = dispatch(action) console .log('dispatch 后:' , middlewareAPI.getState(), '\n' ) return returnValue } } } var enhancedCreateStore = Redux.applyMiddleware(printStateMiddleware)(Redux.createStore)var store = enhancedCreateStore(reducer)store.dispatch(inc()) store.dispatch(inc()) store.dispatch(dec())
上述的案例中,生成 store
的代码中其实可以将中间件放到 createStore
中
1 2 3 4 var store = Redux.createStore( reducer, Redux.applyMiddleware(printStateMiddleware) )
如果有多个中间件以及多个增强器,还可以这样写
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import { createStore, applyMiddleware, compose } from 'redux' const store = createStore( reducer, preloadedState, compose( applyMiddleware( middleware1, middleware2, middleware3 ), enhancer3, enhancer2, enhancer1 ) )
之所以可以这样使用,是因为在 createStore
的源码的开头部分
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 export default function createStore (reducer, preloadedState, enhancer ) { if (typeof preloadedState === 'function' && typeof enhancer === 'undefined' ) { enhancer = preloadedState preloadedState = undefined } if (typeof enhancer !== 'undefined' ) { if (typeof enhancer !== 'function' ) { throw new Error ('Expected the enhancer to be a function.' ) } return enhancer(createStore)(reducer, preloadedState) } if (typeof reducer !== 'function' ) { throw new Error ('Expected the reducer to be a function.' ) } }
如果有多个中间件以及多个增强器(有多个 enhancer
),则注 (1)
中的代码会执行多次,生成最终的超级增强版 store
,比如上例中 compose
内部的执行顺序示意图
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 原 createStore ———————— ↓ return enhancer1(createStore)(reducer, preloadedState, enhancer2) | ├———————— ——> createStore 增强版 1 ↓ return enhancer2(createStore1)(reducer, preloadedState, enhancer3) | ├———————————> createStore 增强版 1 + 2 ↓ return enhancer3(createStore1 + 2 )(reducer, preloadedState, applyMiddleware(m1, m2, m3)) | ├————————————————————————————> createStore 增强版 1 + 2 + 3 ↓ return appleMiddleware(m1, m2, m3)(createStore1 + 2 + 3 )(reducer, preloadedState) | ├——————————————————————————————————> 生成最终增强版 store