redux 中间件 --- applyMiddleware 源码解析 + 中间件的实战
前传 中间件的由来
redux的操作的过程,用户操作的时候,我们通过dispatch分发一个action,纯函数reducer检测到该操作,并根据action的type属性,进行相应的运算,返回state,然后更新view。
但是一个很重要的问题,reducer对于action会立即进行运算,并返回state,如果我们的操作是要获取服务端的数据,需要调用接口类似的异步操作呢?很明显这样操作不行。所以,middleware中间件诞生了,中间件就是处理reducer处理不了的问题,对reducer做一个补充,配合。
我们就以使用中间件来解决异步问题为例来说,中间件顾名思义,就是作为一个流程的一个中间处理程序存在,它是放到一个流程的中的。问题是在处理一部问题,我们把他放哪的问题
a、action只是一个跑腿的,把操作的相关数据带到reducer;
b、reducer只是个干活的,纯函数,计算一下嘛,返回新的数据,但是,异步操作它搞不了;
c、view,理论上这或者是显示的,最好不要有太多的逻辑,中间件肯定不能放;
d、剩下的就是把action分发到reducer的dispatch了,我们可以在分发的时候做点手脚,其实就是对dispatch的重写或者是扩展。
看看applyMiddleware
中间件知道放在哪了,我们看看实现中间件的重要api ----- applyMiddleware 的源码
function applyMiddleware() {
for (var _len = arguments.length, middlewares = Array(_len), _key = 0;
_key < _len; _key++) {
middlewares[_key] = arguments[_key];
}
return function (createStore) { // 以下称这个方法为func1
return function () { // 以下称这个方法为func2
for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
args[_key2] = arguments[_key2];
}
var store = createStore.apply(undefined, args);// 调用createStore方法生成store
// 初始化一个新的dispatch,暂时认为_dispatch = store.dispatch,从效果上看是这样的,暂时不明白为什么这样写,有待大牛指点。
var _dispatch = function dispatch() {
throw new Error('Dispatching while constructing your middleware is not allowed. ' + 'Other middleware would not be applied to this dispatch.');
};
var middlewareAPI = {
getState: store.getState,
dispatch: function dispatch() {
return _dispatch.apply(undefined, arguments);
}
};
//让每一个中间件调用一次,参数为middlewareAPI,并把结果方法chain中,注意这个地方,是中间件的第一层,参数是{getState, dispatch}
var chain = middlewares.map(function (middleware) {
return middleware(middlewareAPI);
});
// 将各个中间件的功能组合到 dispatch 上,生成新的dispatch,注意此时是中间件的第二层, 参数是store.dispatch (关于 compose 解析)
_dispatch = compose.apply(undefined, chain)(store.dispatch);
return _extends({},
store, // 最终返回的是store数据和加强后的dispatch
{dispatch: _dispatch}
);
};
};
}
a、applyMiddleware 方法本身
它首先通过一个for循环,将它的形参以数组元素的形式放到 middleware 中,并返回了一个形参为 createStore 方法(即为标注的func1);
b、形参为 createStore 方法(即为标注的func1)
这货很懒,只是返回了一个有很多参数的方法(即为标注的func2);
c、很多参数的方法(即为标注的func2)
这个方法和 applyMiddleware 一样,它首先通过一个for循环,将它的形参以数组元素的形式放到 args中;
具体源码的解析,请看源码的注释;
中间件的编写
我在源码的解析中,写了两个注意,分别是中间件的第一层和第二层,多以中间件的外面两层应该是如下的:
const timeOutMiddleware = ({ dispatch, getState }) => { return (storeDispatch) => { return { ... }; }; }
根据 compose 的操作原理,每一个中间件,即 chain 中的每一个元素,参数都是前一个中间件的组合后的 dispatch,返回的都是在参数的基础上组合自己功能后的dispatch,chain最后一个元素参数是store.dispatch。因此return的应该是一个通过这些中间件加强后的 dispatch。在此,我们就可以根据action传入的数据进行区分,加入我们中间件具体需要处理的情况。如下:
const timeOutMiddleware = ({ dispatch, getState }) => (storeDispatch) => (action) => { if (typeof action === 'function') { return action(dispatch, getState); } else { return storeDispatch(action); } };
如上,我们如果传入正常的action,我们就执行正常的store.dispatch。如果我们 action 传入的是 function 类型,那么这就是我们中间件处理的情况了。在action的function中我们可以拿到 store 的 dispatch 和 store 的 getState。当然我们在这个方法中可以异步的获取服务端的数据,然后根据成功或者失败的结果,通过 dispatch 再次分发一个正常的 action 同步我们获取到的数据,执行相应的操作。如下
deleteStaff = (data) => { const { dispatch } = this.props; const { staffId } = data; const action = (dispatchd, getState) => { setTimeout(() => { dispatchd({type: 'DELETE', payload: {staffId}}); }, 3000); }; dispatch(action); };
这是一个方法,删除一条数据,通过 setTimeout 模仿异步,在3秒后,再次发一个 action,执行删除数据;
本文栗子的代码:https://github.com/wayaha/react-demos-middleware
(对您有帮助的话,请您帮我点颗 star)