middleware
What:
定义:middleware(中间件),用来简化和隔离这些基础设施和业务逻辑之间的细节,让开发着能够关注在业务的开发上,以提高开发效率.
- 在express和koa等服务端框架中,middleware是指可以被嵌入在框架接收请求到产生响应过程中的代码.(比如:可以在express的middleware中完成添加CORS headers,记录日志,内容压缩等工作.)
- Reaux中,middleware提供的是位于action被发起后,到达reducer之前的扩展点.可以用其进行日志记录,创建崩溃报告,调用异步接口或者路由等等.
这是一个简单的数据流图,点击button分发一个action,进入reducer,reducer接收到action更新state并重新渲染view.
那么,如果我们需要点击button从服务端获取数据,只有在等待获取完成之后,才可进行页面的render,这样会阻塞页面的展示.由此,我们利用middleware来增强dispatch.
使用middleware处理事件的逻辑.
How:
Redux提供了applyMiddleware来装载middleware:
//应用了函数的柯理化(currying),使用一种单参函数来使用参数的方法.
//applyMiddleware
import compose from './compose';
//通过不断currying形成的middleware积累参数,配合compose,形成pipeline处理数据流.
//共享store,因为闭包的存在,在applyMiddleware完成后,middleware内部拿到的store是最新的
export default function applyMiddleware(...middlewares) {
return (next)=>(reducer,initialState)=>{
let store=next(reducer,initialState);
let dispatch=store.dispatch;
let chain=[];
//给middleware分发store
let middleWareAPI={
getState:store.getState,
dispatch:(action)=>dispatch(action)
};
//让每个middleware带着middlewareAPI这个参数执行一遍,然后获得带着相同的store.
chain=middlewares.map(middleware=>middleware(middleWareAPI));
//所有chain中的函数组装成一个新的函数,新的dispatch,然后进行执行.
//newDispatch=fn1(fn2(fn3(store.dispatch)))
dispatch=compose(...chain)(store.dispatch);
return {
...store,
dispatch
};
};
}
//logger
export default store => next => action => {
console.log('dispatch:', action);
next(action);
console.log('finish:', action);
}
粘贴的compose代码:
export default function compose(...funs) {
if (funs.length === 0) {
return arg = arg;
}
if (funs.length === 1) {
return funcs[0];
}
//获取最后一个函数
const last=funs[funs.length - 1];
//获取除最后一个以外的函数中[0,length-1]
const rest = funs.slice(0, -1);
//通过函数curry化
return (...args) => rest.reduceRight((compose, f) => f(composed), last(...args))
}
最后:现在每个middleware都可以访问store,每个middlewareAPI这个变量,拿到diapatch这个属性之后,middleware中调用store.dispatch(),传入action,这样middleware通过next(action)层层处理.往往用于异步请求的需求里.
注:
确保在middleware中调用的是store.dispatch(action)而不是next(action),那么这个操作会再次遍历包含当前middleware在内的整个middleware链.(异步非常有用).
为了保证只能应用一次middleware一次,他作用于createStore()上而不是store本身,因此它的签名不是(store,middlewares)=>store,而是(…middlewares)=>crateStore=>createStore.createStore() 也接受将希望被应用的函数作为最后一个可选参数传入。
最终方法:官网上有七个呐
这里记两个自己感觉好玩的
/**
* 除了 action 之外还可以发起 promise。
* 如果这个 promise 被 resolved,他的结果将被作为 action 发起。
* 这个 promise 会被 `dispatch` 返回,因此调用者可以处理 rejection。
*/
const vanillaPromise=store=>next=>action=>{
if(typeof action.then!=='function'){
return next(action);
}
return Promise.resolve(action).then(store.dispatch)
};
/**
* 用 { meta: { delay: N } } 来让 action 延迟 N 毫秒。
* 让 `dispatch` 返回一个取消 timeout 的函数。
*/
const timeoutScheduler = store => next => action => {
if (!action.meta || !action.meta.delay) {
return next(action);
}
let timeoutId=setTimeout(
()=>next(action),
action.meta.delay
);
return function cancel() {
clearTimeout(timeoutId)
}
};