详解express与koa中间件模式对比

发布时间 - 2026-01-11 02:41:05    点击率:

起因

最近在学习koa的使用, 由于koa是相当基础的web框架,所以一个完整的web应用所需要的东西大都以中间件的形式引入,比如koa-router, koa-view等。在koa的文档里有提到:koa的中间件模式与express的是不一样的,koa是洋葱型,express是直线型,至于为什么这样,网上很多文章并没有具体分析。或者简单的说是async/await的特性之类。先不说这种说法的对错,对于我来说这种说法还是太模糊了。所以我决定通过源码来分析二者中间件实现的原理以及用法的异同。

为了简单起见这里的express用connect代替(实现原理是一致的)

用法

二者都以官网(github)文档为准

connect

下面是官网的用法:

var connect = require('connect');
var http = require('http');

var app = connect();

// gzip/deflate outgoing responses
var compression = require('compression');
app.use(compression());

// store session state in browser cookie
var cookieSession = require('cookie-session');
app.use(cookieSession({
 keys: ['secret1', 'secret2']
}));

// parse urlencoded request bodies into req.body
var bodyParser = require('body-parser');
app.use(bodyParser.urlencoded({extended: false}));

// respond to all requests
app.use(function(req, res){
 res.end('Hello from Connect!\n');
});

//create node.js http server and listen on port
http.createServer(app).listen(3000);

根据文档我们可以看到,connect是提供简单的路由功能的:

app.use('/foo', function fooMiddleware(req, res, next) {
 // req.url starts with "/foo"
 next();
});
app.use('/bar', function barMiddleware(req, res, next) {
 // req.url starts with "/bar"
 next();
});

connect的中间件是线性的,next过后继续寻找下一个中间件,这种模式直觉上也很好理解,中间件就是一系列数组,通过路由匹配来寻找相应路由的处理方法也就是中间件。事实上connect也是这么实现的。

app.use 就是往中间件数组中塞入新的中间件。中间件的执行则依靠私有方法 app.handle 进行处理,express也是相同的道理。

koa

相对connect,koa的中间件模式就不那么直观了,借用网上的图表示:

也就是koa处理完中间件后还会回来走一趟,这就给了我们更加大的操作空间,来看看koa的官网实例:

const Koa = require('koa');
const app = new Koa();

// x-response-time

app.use(async (ctx, next) => {
 const start = Date.now();
 await next();
 const ms = Date.now() - start;
 ctx.set('X-Response-Time', `${ms}ms`);
});

// logger

app.use(async (ctx, next) => {
 const start = Date.now();
 await next();
 const ms = Date.now() - start;
 console.log(`${ctx.method} ${ctx.url} - ${ms}`);
});

// response

app.use(async ctx => {
 ctx.body = 'Hello World';
});

app.listen(3000);

很明显,当koa处理中间件遇到await next()的时候会暂停当前中间件进而处理下一个中间件,最后再回过头来继续处理剩下的任务,虽然说起来很复杂,但是直觉上我们会有一种隐隐熟悉的感觉:不就是回调函数吗。这里暂且不说具体实现方法,但是确实就是回调函数。跟async/await的特性并无任何关系。

源码简析

connect与koa中间件模式区别的核心就在于next的实现,让我们简单看下二者next的实现。

connect

connect的源码相当少加上注释也就200来行,看起来也很清楚,connect中间件处理在于proto.handle这个私有方法,同样next也是在这里实现的

// 中间件索引
var index = 0
function next(err) {


 // 递增
 var layer = stack[index++];

 // 交由其他部分处理
 if (!layer) {
  defer(done, err);
  return;
 }

 // route data
 var path = parseUrl(req).pathname || '/';
 var route = layer.route;

 // 递归
 // skip this layer if the route doesn't match
 if (path.toLowerCase().substr(0, route.length) !== route.toLowerCase()) {
  return next(err);
 }

 // call the layer handle
 call(layer.handle, route, err, req, res, next);
 }

删掉混淆的代码后 我们可以看到next实现也很简洁。一个递归调用顺序寻找中间件。不断的调用next。代码相当简单但是思路却很值得学习。

其中 done 是第三方处理方法。其他处理sub app以及路由的部分都删除了。不是重点

koa

koa将next的实现抽离成了一个单独的包,代码更加简单,但是实现了一个貌似更加复杂的功能

function compose (middleware) {
 return function (context, next) {
 // last called middleware #
 let index = -1
 return dispatch(0)
 function dispatch (i) {
  index = i
  try {
  return Promise.resolve(fn(context, function next () {
   return dispatch(i + 1)
  }))
  } catch (err) {
  return Promise.reject(err)
  }
 }
 }
}

看着上面处理过的的代码 有些同学可能还是会不明觉厉。

那么我们继续处理一下:

function compose (middleware) {

 return function (context, next) {
 // last called middleware #
 let index = -1
 return dispatch(0)
 function dispatch (i) {
  index = i
  let fn = middleware[i]
  if (i === middleware.length) {
  fn = next
  }
  if (!fn) return
  return fn(context, function next () {
  return dispatch(i + 1)
  })
 }
 }
}

这样一来 程序更加简单了 跟async/await也没有任何关系了,让我们看下结果好了

var ms = [
 function foo (ctx, next) {
 console.log('foo1')
 next()
 console.log('foo2')
 },
 function bar (ctx, next) {
 console.log('bar1')
 next()
 console.log('bar2')
 },
 function qux (ctx, next) {
 console.log('qux1')
 next()
 console.log('qux2')
 }
]

compose(ms)()

执行上面的程序我们可以发现依次输出:

foo1
bar1
qux1
qux2
bar2
foo2

同样是所谓koa的洋葱模型,到这里我们就可以得出这样一个结论:koa的中间件模型跟async或者generator并没有实际联系,只是koa强调async优先。所谓中间件暂停也只是回调函数的原因(在我看来promise.then与回调其实没有什么区别,甚至async/await也是回调的一种形式)。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。


# express  # koa  # 对比  # 中间件  # nodejs中Express与Koa2对比分析  # 关于express与koa的使用对比详解  # 浅谈redux  # express 中间件实现对比解析  # node.js中koa和express的差异对比  # 回调  # 递归  # 官网  # 让我们  # 也很  # 下一  # 可以看到  # 文档  # 都以  # 的是  # 看着  # 在这里  # 很好  # 会有  # 成了  # 好了  # 也就  # 也没  # 就不  # 还会 


相关栏目: 【 网站优化151355 】 【 网络推广146373 】 【 网络技术251813 】 【 AI营销90571


相关推荐: 大连 网站制作,大连天途有线官网?  Win11摄像头无法使用怎么办_Win11相机隐私权限开启教程【详解】  Laravel如何处理文件下载请求?(Response示例)  如何用景安虚拟主机手机版绑定域名建站?  Laravel怎么实现模型属性转换Casting_Laravel自动将JSON字段转为数组【技巧】  如何安全更换建站之星模板并保留数据?  Laravel如何实现多表关联模型定义_Laravel多对多关系及中间表数据存取【方法】  Laravel如何使用Service Container和依赖注入?(代码示例)  如何挑选高效建站主机与优质域名?  高端云建站费用究竟需要多少预算?  如何用PHP快速搭建高效网站?分步指南  如何登录建站主机?访问步骤全解析  Laravel怎么生成二维码图片_Laravel集成Simple-QrCode扩展包与参数设置【实战】  Laravel如何实现用户密码重置功能?(完整流程代码)  电视网站制作tvbox接口,云海电视怎样自定义添加电视源?  如何在IIS中新建站点并解决端口绑定冲突?  bootstrap日历插件datetimepicker使用方法  如何在建站宝盒中设置产品搜索功能?  JS中使用new Date(str)创建时间对象不兼容firefox和ie的解决方法(两种)  如何快速生成可下载的建站源码工具?  javascript中对象的定义、使用以及对象和原型链操作小结  教你用AI将一段旋律扩展成一首完整的曲子  mc皮肤壁纸制作器,苹果平板怎么设置自己想要的壁纸我的世界?  微信小程序 五星评分(包括半颗星评分)实例代码  Laravel如何监控和管理失败的队列任务_Laravel失败任务处理与监控  Laravel Artisan命令怎么自定义_创建自己的Laravel命令行工具完全指南  Laravel怎么返回JSON格式数据_Laravel API资源Response响应格式化【技巧】  惠州网站建设制作推广,惠州市华视达文化传媒有限公司怎么样?  Claude怎样写结构化提示词_Claude结构化提示词写法【教程】  Laravel如何使用Facades(门面)及其工作原理_Laravel门面模式与底层机制  详解免费开源的.NET多类型文件解压缩组件SharpZipLib(.NET组件介绍之七)  Laravel如何使用Scope本地作用域_Laravel模型常用查询逻辑封装技巧【手册】  Laravel如何设置自定义的日志文件名_Laravel根据日期或用户ID生成动态日志【技巧】  zabbix利用python脚本发送报警邮件的方法  详解Android中Activity的四大启动模式实验简述  Laravel如何使用Guzzle调用外部接口_Laravel发起HTTP请求与JSON数据解析【详解】  php嵌入式断网后怎么恢复_php检测网络重连并恢复硬件控制【操作】  高防网站服务器:DDoS防御与BGP线路的AI智能防护方案  Laravel如何处理CORS跨域请求?(配置示例)  Laravel Session怎么存储_Laravel Session驱动配置详解  黑客如何通过漏洞一步步攻陷网站服务器?  JS碰撞运动实现方法详解  高配服务器限时抢购:企业级配置与回收服务一站式优惠方案  Javascript中的事件循环是如何工作的_如何利用Javascript事件循环优化异步代码?  如何在景安服务器上快速搭建个人网站?  网站制作怎么样才能赚钱,用自己的电脑做服务器架设网站有什么利弊,能赚钱吗?  Windows10怎样连接蓝牙设备_Windows10蓝牙连接步骤【教程】  5种Android数据存储方式汇总  Android使用GridView实现日历的简单功能  长沙做网站要多少钱,长沙国安网络怎么样?