本文是关于 redux(3.7.2)源代码的一些浅读
|-- utils/ |-- warning.js //打印error |-- 1. applyMiddleware.js // |-- 2. bindActionCreators.js // |-- 3. combineReducers.js // |-- 4. compose.js // |-- 5. createStore.js // |-- index.js //入口文件
与文件对应的,主要也是介绍 createStore compose combineReducers bindActionCreators applyMiddleware这几个函数。
<!--more-->
1. compose
先来看看compose函数,这个比较简单。
export default function compose() { //funcs保存着所有参数函数的数组 for (var _len = arguments.length,funcs = Array(_len),_key = 0; _key < _len; _key++) { funcs[_key] = arguments[_key]; } //省略一些逻辑判断…… //reduce方法使用指定的函数将数组元素进行组合,生成单个值 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 与 中间件链
故名思义这个函数就是应用中间件,一般使用方式为:
//thunk代表react-thunk中间件 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的源码:
//react-thunk2.2.0的代码,很简单 只有十几行…… //外层函数可以传入多余的参数 function createThunkMiddleware(extraArgument) { //获取dispatch getState(是由applyMiddleware传入) return function (_ref) { var dispatch = _ref.dispatch,getState = _ref.getState; //1. 如果这个中间件是调用链最内环的,next指原store.dispatch //2. 其他next一般指上一个中间件的返回值 action => {} //对于这个next的赋值不清楚的话可以结合之后的applyMiddleware函数 return function (next) { return function (action) { //1. 原action只是个普通的对象,thunk使action可以传入函数类型,并传入了dispatch,getState,extraArgument //2. 如果action是个异步函数,thunk会调用该函数 if (typeof action === 'function') { return action(dispatch,extraArgument); } //3. 函数调用结束后,获取必要的数据再次触发dispatch由此实现异步效果。 return next(action); }; }; }; }
可以看到react-thunk中间件是一个多层的高阶函数,格式大致为:
({dispatch,getState}) => next => action => {return next(action)}
中间件的格式都要遵循这样相似的格式,这是由applyMiddleware函数决定的。接下来看下applyMiddleware的源码:
2.2 applyMiddleware源码
//applyMiddleware源码 export default function applyMiddleware() { //middlewares保存传进来的中间件 for (var _len = arguments.length,middlewares = Array(_len),_key = 0; _key < _len; _key++) { middlewares[_key] = arguments[_key]; } //createStore是创建createStore的函数,会在下文解读,这里先不管 return function (createStore) { return function (reducer,preloadedState,enhancer) { //创建store 并获取了其dispatch方法 var store = createStore(reducer,enhancer); var _dispatch = store.dispatch; var chain = []; //用于传递给中间件第一层函数的参数,上文在thunk中有看到 var middlewareAPI = { getState: store.getState,dispatch: function dispatch(action) { return _dispatch(action); } }; //middlewares保存的是中间件,chain对应保存的就是中间件第二层函数组成的数组 //形象点就是上文中间件格式去掉第一层:next => action => {} chain = middlewares.map(function (middleware) { return middleware(middlewareAPI); }); //1. componse的功能在上文说到,假设chain为[F1,F2,F3],compose之后变成了F1(F2(F3)) //2. 与上文thunk中说到中间件格式对应,F3是中间件链的最内环 所以F3的next参数为store.dispatch //3. F2的next参数就是F3返回的 action => {} //4. 同样的F1的next参数就是F2返回的 action => {} // //_dispatch就相当于F1(F2(F3(store.dispatch))) //这样多个中间件就组合到了一起,形成了中间件链 _dispatch = compose.apply(undefined,chain)(store.dispatch); //新的dispatch会覆盖原dispatch,之后调用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)}
写三个中间件:
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一起加入中间件链:
const middlewares = [ middleware1,thunkMiddleWare,middleware2,middleware3,loggerMiddleWare,//redux-logger需要放在最后面 ]; ... const store = createStore(rootReducer,applyMiddleware(...middlewares));
运行你的代码后在chrome控制台看到如下信息:
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,extraArgument); } return next(action); }
这也验证了上文对 _dispatch = compose.apply(undefined,chain)(store.dispatch);
的理解。
这里的中间件链是由 [m1,thunk,m2,m3,logger]
组成,在compose
之后变成了 m1(thunk(m2(m3(logger))))
,
所以
三、 执行一下 dispatch(action) :
这时候分两种情况:
1、当action类型为对象时,在chrome控制台看到如下信息:
发起 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控制台看到如下信息:
发起 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
。
//这个函数的主要作用就是返回一个函数,当我们调用返回的这个函数的时候,就会自动的dispatch对应的action function bindActionCreator(actionCreator,dispatch) { return function () { return dispatch(actionCreator.apply(undefined,arguments)); }; } export default function bindActionCreators(actionCreators,dispatch) { //省略一些逻辑判断 // 获取所有action creater函数的名字 var keys = Object.keys(actionCreators); // 保存dispatch和action creater函数进行绑定之后的集合 var boundActionCreators = {}; //为每个actionCreators 包装上 dispatch 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函数。
//combineReducers的代码比较简单,在省略一些错误判断的代码后: 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); //从上面到这里都是为了保存finalReducerKeys 和 finalReducers ...... //返回的combination函数就相当于结合所有reducers之后新的reducer return function combination() { var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var action = arguments[1]; ...... var hasChanged = false; var nextState = {}; //这里遍历了所有之前自定义的reducers,并记录下是否state有改变,并记录下改变的state for (var _i = 0; _i < finalReducerKeys.length; _i++) { var _key = finalReducerKeys[_i]; var reducer = finalReducers[_key]; var prevIoUsStateForKey = state[_key]; //遍历所有的reducer,若prevIoUsStateForKey匹配到则返回新的state //若匹配不到就在reducer中dufault中返回原state var nextStateForKey = reducer(prevIoUsStateForKey,action); ...... nextState[_key] = nextStateForKey; //这里有点意思,并没有因为找到不同的state就直接返回 //这意味着,多个子reducers可以对同个action返回自己的state //并且返回的state是依据靠后的reducer的返回值决定的 hasChanged = hasChanged || nextStateForKey !== prevIoUsStateForKey; } return hasChanged ? nextState : state; }; }
5. createStore
现在来看下最重要的createStore函数:
<!--preloadedState-->
/* * redux有且仅有一个store,createStore函数就是用于创建一个store用来存放所有的state。 * @param {Function} reducer * @param {any} [preloadedState] 初始化state * @param {Function} [enhancer] store的增强器,有applyMiddleware、时间旅行(time travel)、持久化(persistence)等。 */
上面是createStore的参数,其中enhancer指的是store的增强器(对store API进行改造)。
- applyMiddleware在前面我们已经见过了,applyMiddleware是对store的dispatch函数的修改。
-
时间旅行则是指redux可以回到任意以前的状态。
- 持久化持久化这个概念肯定大家都熟悉,也有人做出了实现:redux-persist
介绍完enhancer,来接着看代码逻辑:
export default function createStore(reducer,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; //当前的state var currentListeners = []; //订阅函数 var nextListeners = currentListeners;//订阅函数备份, //用于解决listeners数组执行过程(for循环)中,取消订阅listener产生的listeners数组index错误。 //这样保证在某个dispatch后,会保证在这个dispatch之前的所有事件监听器全部执行 var isDispatching = false; //dispatch方法同步标志 function ensureCanMutateNextListeners() { if (nextListeners === currentListeners) { nextListeners = currentListeners.slice(); } } /** * @returns {any} 当前state */ function getState() { return currentState; } /* * 将一个订阅函数放到 listeners 队列里,当 dispatch 的时候,逐一调用 listeners 中的回调方法。 * @param {Function} listener函数 * @return {Function} 解除绑定的方法 */ 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); }; } /** * dispatch方法,调用reducer * * @param {Object} action * * @returns {Object} 一般会返回action,* 如果使用了中间件,可能返回promise 或者function之类的() */ function dispatch(action) { ...... //dispatch是同步的,用isDispatching标志来判断 if (isDispatching) { throw new Error('Reducers may not dispatch actions.'); } //调用reducer try { isDispatching = true; //更新state树 currentState = currentReducer(currentState,action); } finally { isDispatching = false; } //调用nextListeners中的监听方法 var listeners = currentListeners = nextListeners; for (var i = 0; i < listeners.length; i++) { var listener = listeners[i]; listener(); } return action; } /** * 替换reducer */ function replaceReducer(nextReducer) { if (typeof nextReducer !== 'function') { throw new Error('Expected the nextReducer to be a function.'); } currentReducer = nextReducer; //触发生成新的state树 dispatch({ type: ActionTypes.INIT }); } /** *略 */ function observable() { ...... } // 生成初始state树 dispatch({ type: ActionTypes.INIT }); return _ref2 = { dispatch: dispatch,subscribe: subscribe,getState: getState,replaceReducer: replaceReducer },_ref2[$$observable] = observable,_ref2; }
以上就是我个人的简单见解,如果有什么错误之处,敬请指导讨论
参考资料: