摘要:前言目前最新版本是所以本文分析也基于这个版本。源码分析直接切入主题由于目前是一个独立的路由和中间件框架。所以分析的方向也以这两个为主。源码去年的时候有分析过现在对比分析思考下。
前言
目前express最新版本是4.16.2,所以本文分析也基于这个版本。目前从npm仓库上来看express使用量挺高的,express月下载量约为koa的40倍。所以目前研究下express还是有一定意义的。
源码分析直接切入主题,由于目前express是一个独立的路由和中间件web框架。所以分析的方向也以这两个为主。源码的研究只注重关键步骤和流程思想, 具体的hack,异常,边界处理不做过多精力关注。
关键步骤 中间件的执行/** * //负责具体请求的逻辑处理,当一维layer中所对应的stack执行完后执行done(目的是为了一维路由列表中进行下一个路由的匹配执行)。 * @param done(路由中的next) */ Route.prototype.dispatch = function dispatch(req, res, done) { var idx = 0; var stack = this.stack; next(); //递归方式执行stack中的layer,通过next控制流程的执行 function next(err) { // 出错直接退出当前stack和路由列表中回调的后续执行 if (err && err === "router") { return done(err) } //出错直接退出当前stack列表后续执行,进行下一个路由匹配 if (err && err === "route") { return done(); } var layer = stack[idx++]; //执行结束 if (!layer) { return done(err); } if (layer.method && layer.method !== method) { return next(err); } //调用具体注册好的逻辑 if (err) { layer.handle_error(err, req, res, next); } else { layer.handle_request(req, res, next); } } };
说明 上面是源码的部分摘要,去除了无关的信息,对关键步骤加了注解。
路由匹配/** * 对具体请求进行路由分发处理 * out 是最后一个处理器,默认是请求的回调,不传的话是内部提供的error handlers */ proto.handle = function handle(req, res, out) { var self = this; var idx = 0; var paramcalled = {}; // middleware 和 roues var stack = self.stack; var done = restore(out, req, "baseUrl", "next", "params"); next(); //递归方式遍历注册的一维路由 function next(err) { var layerError = err === "route" ? null : err; var layer,match,route; //取出注册好的路由,进行请求匹配 while (match !== true && idx < stack.length) { layer = stack[idx++]; //req的path匹配,如果有注册参数路由会解析参数到layer.params上 match = matchLayer(layer, path); route = layer.route; } if (match !== true) { return done(layerError); } //将解析好的路径参数放到请求对象上,以便后续参数回调逻辑的使用 req.params = layer.params; //路径参数回调回调 ,例如请求 /user/1 ,参数回调 app.param("user",cb1) app.get("/user/:user",cb2) 会先执行注册的param cb1,然后才会是router中注册的cb2 self.process_params(layer, paramcalled, req, res, function (err) { if (err) { return next(layerError || err); } if (route) { return layer.handle_request(req, res, next); } }); } };
说明 上面摘取了请求进行路由分发处理的关键步骤,并做了相应的注解。
执行流程当一个请求过来的时候交给handler方法,进行路由的匹配,以递归的方式遍历(路由匹配一节中介绍过),当匹配到某一个路由的时候在dispatch执行,web应用启动初始化前注册好的回调逻辑,执行的方式也是以递归的方式(中间件执行一节中介绍过)。
请求匹配执行逻辑已经介绍过了,下面结合着初始化的逻辑,进行分析,具体如下图所示。
说明
初始化针对上图做一下说明,启动服务应用的时候先进行初始化,注册"/jsdt"的get请求,并给路由绑定相应的回调。其中我们的路由放在一维的layer中,每一层路由,例如"/jsdt"又对应一个处理列表,这个二维列表里又存储着一系列layer(具体的回调处理逻辑),因为一维layer中已经记录了路径"/jsdt",所以二维的layer中就不用记录路径了,给个默认值"/",以保持一维layer和二维layer中结构的统一。
请求当一个请求过来的时候先去一维layer所存储的路由中进行路径匹配,以递归的方式。匹配到了,通过dispatch方法,在执行路由所对应的二维layer中的回调逻辑,也是以递归的方式执行,在递归的过程中,如果发生异常,通过路由中传递过来的out,直接进行下一个路由的匹配。
/** *递归执行stack中的handle * @param out 路由中的next */ Route.prototype.dispatch = function (req, res, out) (xxx)
当执行遇到res.end()的时候,数据响应完后,整个请求响应过程就结束了。
koa vs expresskao源码去年的时候有分析过,现在对比分析思考下。express中间件和koa中间件区别,以及与redux中间件区别 ?
koa比较迷你,微内核,拓展性强,所以一些web框架例如阿里的eggjs就是基于koa。而express集成了路由和static中间件所以显得重一些。
网上很多文章都说一个是线性的,一个是洋葱模型。因为两个我都研究过,我觉的这种说法不对,其实执行的时候都是洋葱形。主要的区别是koa内核底层原生支持async语法,koa中的middlewares经过compose以后,每次执行到await next返回的都是一个promise,所以我们可以在顶层加一个try……catch进行异常捕获,这算是koa比较方便的一点。还有一点很多人提到过说koa可以记录处理时间,那是因为每次res.body赋值的时候并没有res.end,所以在第一个中间件很容易记录处理的时间,如下所示,在中间件执行完后,在handleResponse中才会将请求处理结果返回给客户端。
fn(ctx)[是一个立即状态的promise].then(handleResponse).catch(onerror);
而express每次res.send的时候数据已经发给客户端了,当然也可以实现这种需求,只不过没有koa方便。多说几句,其实java中也有类似的实现,例如java中的aop,过滤器,将通用的逻辑,例如日志,权限等模块通过配置的方式进行灵活的插入,配置在xml中。比较灵活,每个模块之间互相解耦,根据需求实现可插拔效果。
在说一下与react中redux中间件机制区别,redux中的applyMiddleware中间件机制,可以在处理store前后加一些通用处理,利用高阶函数compose,通过reduce将多个函数组合成一个可执行执行函数
//applyMiddleware.js chain = middlewares.map(middleware => middleware(middlewareAPI)) dispatch = compose(...chain)(store.dispatch)
//compose.js export default function compose(...funcs) { return funcs.reduce((a, b) => (...args) => a(b(...args))) }
组合好最后执行的时候是洋葱模型效果,举个例子 compose(a, b, c)变成 a(b(c())),而a,b,c的结构类似下面这种形式
function fnA(next) { return function() { console.log("fnA start") next() console.log("fnA end") } }
所以a(b(c()))()执行的时候,就会通过next来控制调用下一个中间件,整体的执行等价于express递归调用方式
express路由和 vue路由共同点vue中也有路由,官方的vue-router,他解决的是url和模板组件匹配渲染的问题, 而express中解决的url和handler匹配执行的问题,而koa内核里面没有集成路由。 从vue-router和express路由中可以看出路由的共性,是为了解决路径和相应处理逻辑的匹配问题在web开发中。
toy版本express根据上述分析的逻辑,实现了一个简化版的express ,融入了express的核心思想,有详尽的步骤注释,有需要的可以参考下。
参考源码版本说明
express 4.16.2
koa 2.2
redux 3.7.2
参考链接
https://github.com/koajs/koa/...
https://www.reddit.com/r/node...
https://blog.jscrambler.com/m...
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/93403.html
摘要:以前其实写过一篇和的对比但是后来发现里面有不少谬误所以一直惦记着纠正一下之前的错误尤其关于中间件部分的对比这里的就拿更加简单的代替的执行流程通常我们都说的中间件模型是线性的也就是一个一个往下执行的如下图这么说当然是没错的但是当我们执行下面代 以前其实写过一篇express和koa的对比, 但是后来发现里面有不少谬误. 所以一直惦记着纠正一下之前的错误, 尤其关于中间件部分的对比. 这里...
摘要:我们分别使用这样的原则来测试向每个架构注入个静态路由,测试最末尾的那个。而我们如何做到达到的性能,主要我们在内存中维护了一份静态路由列表,能让程序以最快的速度找到我们需要的。 对比 如果使用nodejs来搭建Service服务,那么我们首选express或者koa,而fastify告诉我们一个数据: Framework Version Router? Requests/sec ...
摘要:使用承诺和异步功能来摆脱回调地狱的应用程序,并简化错误处理。它暴露了自己的和对象,而不是的和对象。因此,可被视为的模块的抽象,其中是的应用程序框架。这使得中间件对于整个堆栈而言不仅仅是最终应用程序代码,而且更易于书写,并更不容易出错。 Koa 与 Express 此系列文章的应用示例已发布于 GitHub: koa-docs-Zh-CN. 可以 Fork 帮助改进或 Star 关注更新...
摘要:前言一直混迹社区突然发现自己收藏了不少好文但是管理起来有点混乱所以将前端主流技术做了一个书签整理不求最多最全但求最实用。 前言 一直混迹社区,突然发现自己收藏了不少好文但是管理起来有点混乱; 所以将前端主流技术做了一个书签整理,不求最多最全,但求最实用。 书签源码 书签导入浏览器效果截图showImg(https://segmentfault.com/img/bVbg41b?w=107...
摘要:的网站仍然使用有漏洞库上周发布了开源社区安全现状报告,发现随着开源社区的日渐活跃,开源代码中包含的安全漏洞以及影响的范围也在不断扩大。与应用安全是流行的服务端框架,本文即是介绍如何使用以及其他的框架来增强应用的安全性。 showImg(https://segmentfault.com/img/remote/1460000012181337?w=1240&h=826); 前端每周清单专注...
阅读 1563·2021-09-08 10:42
阅读 3573·2021-08-11 10:23
阅读 3860·2019-08-30 14:10
阅读 2693·2019-08-29 17:29
阅读 3057·2019-08-29 12:50
阅读 605·2019-08-26 13:36
阅读 3405·2019-08-26 11:59
阅读 1455·2019-08-23 16:23