本文是关于 redux(3.7.2)源代码的一些浅读
在redux源码目录 中 ,可以看到以下文件目录:
1 2 3 4 5 6 7 8 |-- utils/ |-- warning.js |-- 1. applyMiddleware.js |-- 2. bindActionCreators.js |-- 3. combineReducers.js |-- 4. compose.js |-- 5. createStore.js |-- index.js
与文件对应的,主要也是介绍 createStore compose combineReducers bindActionCreators applyMiddleware这几个函数。
1. compose 先来看看compose函数,这个比较简单。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 export default function compose ( ) { for (var _len = arguments .length , funcs = Array (_len), _key = 0 ; _key < _len; _key++) { funcs[_key] = arguments [_key]; } return funcs.reduce (function (a, b ) { return function ( ) { return a (b.apply (undefined , arguments )); }; }); }
其中主要就是对 reduce 的理解了。reduce()方法使用指定的函数将数组元素进行组合(从左到右),生成单个值。具体可以查看Array.prototype.reduce() | MDN
compose的功能为从右到左,组合参数(函数)。传递给compose方法的参数是函数类型的,
compose(f, g, h) 相当于 (…args) => f(g(h(…args)))
2. applyMiddleware 与 中间件链 故名思义这个函数就是应用中间件,一般使用方式为:
1 2 const store = createStore (rootReducer, initialState, applyMiddleware (thunk));
2.1 中间件及react-thunk 在看applyMiddleware函数前,先来简单看一下中间件,redux中间件在发起action到达reducer之间扩展了一些功能。一般用于记录日志(reudx-logger)和增加异步调用(redux-thunk , redux-saga)等。这些中间件一般按照一定的格式书写,比如react-thunk2.2.0的源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 function createThunkMiddleware (extraArgument ) { return function (_ref ) { var dispatch = _ref.dispatch , getState = _ref.getState ; return function (next ) { return function (action ) { if (typeof action === 'function' ) { return action (dispatch, getState, extraArgument); } return next (action); }; }; }; }
可以看到react-thunk中间件是一个多层的高阶函数,格式大致为:
1 ({dispatch,getState}) => next => action => {return next (action)}
中间件的格式都要遵循这样相似的格式,这是由applyMiddleware函数决定的。接下来看下applyMiddleware的源码:
2.2 applyMiddleware源码 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 export default function applyMiddleware ( ) { for (var _len = arguments .length , middlewares = Array (_len), _key = 0 ; _key < _len; _key++) { middlewares[_key] = arguments [_key]; } 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 : function dispatch (action ) { return _dispatch (action); } }; chain = middlewares.map (function (middleware ) { return middleware (middlewareAPI); }); _dispatch = compose.apply (undefined , chain)(store.dispatch ); return _extends ({}, store, { dispatch : _dispatch }); }; }; }
在_dispatch = compose.apply(undefined, chain)(store.dispatch);
组合后,中间件的next就是store.dispatch一路经过上一个中间件封装后的变种dispatch。
2.3 中间件链的执行顺序 光看代码可能对于中间件链的执行顺序的理解还是优点蒙,这里来做个实践加深一下理解。
一、 先按上文说的中间件格式({dispatch,getState}) => next => action => {return next(action)}
写三个中间件:
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 function middleware1 ({dispatch,getState} ) { return function (next ) { console .log ('middleware1 next层 next:' ,next); return function (action ) { console .log ('middleware1 action层 开始' ) next (action) console .log ('middleware1 action层 结束' ) } } } function middleware2 ({dispatch,getState} ) { return function (next ) { console .log ('middleware2 next层 next:' ,next); return function (action ) { console .log ('middleware2 action层 开始' ) next (action) console .log ('middleware2 action层 结束' ) } } } function middleware3 ({dispatch,getState} ) { return function (next ) { console .log ('middleware3 next层 next:' ,next); return function (action ) { console .log ('middleware3 action层 开始' ) next (action) console .log ('middleware3 action层 结束' ) } } }
二、 将它们和redux-thunk 和 redux-logger一起加入中间件链:
1 2 3 4 5 6 7 8 9 10 11 const middlewares = [ middleware1, thunkMiddleWare, middleware2, middleware3, loggerMiddleWare, ]; ... const store = createStore (rootReducer, initialState, applyMiddleware (...middlewares));
运行你的代码后在chrome控制台看到如下信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 middleware3 next层 next : ƒ (l){if ("function" ==typeof … middleware2 next层 next : ƒ (action){ console .log ('middleware3 action层 开始' );next (action);console .log ('middleware3 action层 结束' );} middleware1 next层 next : ƒ (action) { if (typeof action === 'function' ) { return action (dispatch, getState, extraArgument); } return next (action); }
这也验证了上文对 _dispatch = compose.apply(undefined, chain)(store.dispatch);
的理解。
这里的中间件链是由 [m1,thunk,m2,m3,logger]
组成,在compose
之后变成了 m1(thunk(m2(m3(logger))))
, 所以
m3的next 参数是logger 的action层函数(参数为action那层函数)
m2的next 参数是m3 的action层函数
m1的next 参数是thunk 的action层函数
三、 执行一下 dispatch(action) :
这时候分两种情况:
1、当action类型为对象时,在chrome控制台看到如下信息:
1 2 3 4 5 6 7 8 9 发起 dispatch (action) action类型为对象 middleware1 action层 开始 middleware2 action层 开始 middleware3 action层 开始 action SOME_OBJ_ACTION redux-logger.js :1 middleware3 action层 结束 middleware2 action层 结束 middleware1 action层 结束
粗粗一看好像顺序不对啊,不该先执行middleware3的逻辑嘛?其实内层(位置靠后的中间件)只是返回了一个function,并没有执行其中的逻辑,不断由外层的中间件包裹形成了一个‘洋葱模型 ’。由外向内穿心而过,再由内向外完成流程。
这样子就很明确了,中间件的action层执行顺序为先加入中间件链的先执行! ,更准确的说中间件中先执行从外层向内层中 next(action)
之前的逻辑,然后执行从内层向外层中 next(action)
之后的逻辑。
2、当action类型为函数时,在chrome控制台看到如下信息:
1 2 3 4 发起 dispatch (action) action类型为函数 middleware1 action层 开始 middleware1 action层 结束
可以看到只执行了 middleware1 和 thunk 两个中间件!这是因为thunk没有执行 next(action)
中断了中间件链! 当中间件没有执行next(action)时会导致中间件链中断,这是因为dispatch没有传递下去,所以中间件还可以捣乱咯~
3. bindActionCreators bindActionCreators的功能是为action creaters包装上dispatch,使其调用action时自动dispatch对应的action。
在bindActionCreators.js中只有两个函数 bindActionCreator
和 bindActionCreators
。
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 function bindActionCreator (actionCreator, dispatch ) { return function ( ) { return dispatch (actionCreator.apply (undefined , arguments )); }; } 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; }
4. combineReducers Reducer只是一些纯函数,它接收之前的state和action,并返回新的state。当应用变大,我们可以拆分多个小的reducers,分别独立的操作state tree的不同部分。而combineReducers就是将多个不同的reducer合并成一个最终的reducer,用于赋值给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 export default 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 ( ) { var state = arguments .length > 0 && arguments [0 ] !== undefined ? arguments [0 ] : {}; var action = arguments [1 ]; ...... 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; }; }
5. createStore 现在来看下最重要的createStore函数:
上面是createStore的参数,其中enhancer指的是store的增强器(对store API进行改造)。
applyMiddleware在前面我们已经见过了,applyMiddleware是对store的dispatch函数的修改。
时间旅行 则是指redux可以回到任意以前的状态。
这是因为Redux使用简单的对象来表示state状态,并使用纯函数计算下一个状态。这意味着如果给定一个特定state状态和一个特定action操作,那么下一个状态将始终完全相同。这种特性可以让我们将所有修改过的状态保存在一个状态历史对象中,通过指定恢复到状态历史对象中从前的状态,来完成时间旅行。
比较容易可以想到的应用就是一些撤销/重做操作。
持久化 持久化这个概念肯定大家都熟悉,也有人做出了实现:redux-persist
介绍完enhancer,来接着看代码逻辑:
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 export default function createStore (reducer, preloadedState, enhancer ) { var _ref2; 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); } ...... 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 (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++) { var listener = listeners[i]; listener (); } 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 _ref2 = { dispatch : dispatch, subscribe : subscribe, getState : getState, replaceReducer : replaceReducer }, _ref2[$$observable] = observable, _ref2; }
以上就是我个人的简单见解,如果有什么错误之处,敬请指导讨论
参考资料: