【Web技术】904- Express/Koa/Redux三者中间件对比
Author:AddOneG Link:http://yoursite.com/2018/09/14/express-koa-redux三者中间件对比/ 这三者对各自的中间件有着不同的实现,作者本人对此也比较好奇,在这里小小的研究一下源码,探究三者之间的异同 什么是中间件 在我看来,中间件就是在你的代码运行中进行一些修改的工具。比如你想喝水,那么喝水之前你将水净化就可以理解为是一次中间件的执行。他不是插件,独立于程序之外,而更像是在你的代码中表现一种类似连接的功能 Koa 与 Express 中间件概述 这两者都是Node层面的,这里我们根据官方文档来对比 Express varapp=express();//没有挂载路径的中间件,应用的每个请求都会执行该中间件app.use(function(req,res,next){console.log('Time:',Date.now());next();});//挂载至/user/:id的中间件,任何指向/user/:id的请求都会执行它app.use('/user/:id',function(req,res,next){console.log('RequestType:',req.method);next();});//路由和句柄函数(中间件系统),处理指向/user/:id的GET请求app.get('/user/:id',function(req,res,next){res.send('USER');}); 可以看到express的中间件是使用next进行线性调用的,一个接着一个的执行,是一种尾递归的调用(后文会讲)。然后在最后一个中间件中进行对response的处理(习惯) Koa constKoa=require('koa');constapp=newKoa();//x-response-timeapp.use(async(ctx,next)=>{conststart=Date.now();awaitnext();constms=Date.now()-start;ctx.set('X-Response-Time',`${ms}ms`);});//loggerapp.use(async(ctx,next)=>{conststart=Date.now();awaitnext();constms=Date.now()-start;console.log(`${ctx.method}${ctx.url}-${ms}`);});//responseapp.use(asyncctx=>{ctx.body='HelloWorld';});app.listen(3000); 从代码中的await可以看出,koa的中间件绝对不是线性的,因为一旦使用了await,代码就会停止当前中间件的执行转而去执行await后面的代码,这里next表示下一个中间件。所以这是一个支持generator的洋葱圈模型(后文会讲) Koa 与 Express 中间件源码进一步解析 上面提到,express的中间件是尾递归调用,而koa的中间件因为使用了await所以是支持generator的洋葱圈模型,这里以此展开来分析代码 Express 我们直接进入application.js中观察中间件处理 app.handle=function(req,res,callback){varstack=this.stack;varidx=0;functionnext(err){if(idx>=stack.length){callback('err')return;}varmid;while(idx<stack.length){mid=stack[idx++];mid(req,res,next);}}next()} 这里next方法不断取出stack中的中间件并且将自己传递给中间件作为参数,这样中间件只需要调用next方法就能不断传递到下一个中间件。在函数的末尾递归调用了next方法,所以称为尾递归调用 Koa Koa对中间件的处理是在一个独立的包koa-compose中 'usestrict'module.exports=composefunctioncompose(middleware){returnfunction(context,next){letindex=-1returndispatch(0)functiondispatch(i){index=iletfn=middleware[i]if(i===middleware.length)fn=nextif(!fn)returnPromise.resolve()try{returnPromise.resolve(fn(context,dispatch.bind(null,i+1)));}catch(err){returnPromise.reject(err)}}}} Koa中使用了Promise来支持异步,这里不停调用dispatch.bind(null, i + 1)传递下一个中间件,一个一个中间件向里执行,直到最后一个中间件执行完resolve掉,然后不断向前resolve中间件,直到第一个中间件被resolve。我们可以发现,相应的处理并不在中间件中而是在其resolve后 Redux 对于redux的基础createStore,reducer,dispatch等就不解释了,这> 里直接看applyMiddleware的代码 importcomposefrom'./compose'exportdefaultfunctionapplyMiddleware(...middlewares){returncreateStore=>(...args)=>{conststore=createStore(...args)letdispatch=()=>{thrownewError(`Dispatchingwhileconstructingyourmiddlewareisnotallowed.`+`Othermiddlewarewouldnotbeappliedtothisdispatch.`)}constmiddlewareAPI={getState:store.getState,dispatch:(...args)=>dispatch(...args)}constchain=middlewares.map(middleware=>middleware(middlewareAPI))dispatch=compose(...chain)(store.dispatch)return{...store,dispatch}}} 这里还是比较好理解的,middlewareAPI中包含两个api,一个是store的getState;另一个是覆写的dispath,这是一个外部变量,最终指向覆写后的dispach,对于compose的作用是compose(f, g, h) 返回 () => f(g(h(..args))) 那么dispatch = compose(...chain)(store.dispatch)即原生的 store.dispatch 传入最后一个“中间件”,返回一个新的dispatch ``, 再向外传递到前一个中间件,直至返回最终的dispatch`, 当覆写后的dispatch调用时,每个“中间件“的执行又是从外向内的”洋葱圈“模型 1. JavaScript 重温系列(22篇全) 2. ECMAScript 重温系列(10篇全) 3. JavaScript设计模式 重温系列(9篇全) 4. 正则 / 框架 / 算法等 重温系列(16篇全) 5. Webpack4 入门(上) || Webpack4 入门(下) 6. MobX 入门(上) || MobX 入门(下) 7. 100 +篇原创系列汇总 回复“加群”与大佬们一起交流学习~ 点击“阅读原文”查看 100+ 篇原创文章 本文分享自微信公众号 - 前端自习课(FE-study)。如有侵权,请联系 support@oschina.cn 删除。本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。