当前位置: 首页 > 后端技术 > Node.js

深入koa源码(一):架构设计

时间:2023-04-03 15:03:07 Node.js

专注于前端和算法系列的干货分享,欢迎关注(???):《微信公众号:心坛博客》|新坛网|最近在github上看了koa的源码,明确了架构设计和使用的第三方库。本系列将分为3篇文章,分别介绍koa的架构设计和3个核心库的原理,最后手工实现一个简单的koa。koa的实现在仓库的lib目录下。如下图,只有4个文件:对于这4个文件,按照用途和封装逻辑可以分为3类:req和res,context和application。req和res对应的文件是:request.js和response.js。分别代表客户端请求信息和服务端返回信息。这两个文件在实现逻辑上是完全一致的。对外暴露的是一个对象,对象上的属性使用getters或者setters来实现读写控制。context对应的文件是:context.js。保存运行环境的上下文信息,比如cookies。另外,由于request和response都属于上下文信息,所以使用了delegate.js库来实现所有属性对request.js和response.js的代理。例如下面的代码:/***响应委托。*/delegate(proto,"response").method("attachment").method("redirect");/***请求委托。*/delegate(proto,"request").method("acceptsLanguages").method("acceptsEncodings");使用代理的另一个好处是可以更方便地访问req和res上的属性。比如在开发koa应用时,可以使用ctx.headers来读取客户端请求的header信息,而不用写ctx.res.headers(这是正确的)。注意:req和res在context.js中并没有绑定到context,而是绑定到application中的context变量ctx。原因是因为每个请求的req/res都不相同。Application对应的文件是:application.js。这个文件的逻辑是最重要的。它的主要作用是将服务启动接口暴露给用户,为每个请求生成一个新的上下文处理中间件,串联起来暴露接口。在使用koa的时候,我们经常会通过listen或者callback来启动服务端:constapp=newKoa();app.listen(3000);//开始监听http.createServer(app.callback()).listen(3000);//callbackstarts这两种启动方式是完全等价的。因为在listen方法内部,回调被调用并传递给http.createServer。然后看看回调方法主要做了什么:调用koa-compose串联中间件(后面会详细介绍)。生成传递给http.createServer()的函数并返回。http.createServer传递给函数参数的请求信息和返回信息都是通过该函数获取的。并将其传递给createContext方法以生成此请求的上下文。将生成的上下文传递给第1步生成的中间件调用链,这就是为什么我们可以在中间件处理逻辑的时候访问ctx来生成新的上下文。这里的context方法对应的是createContext方法。这里我觉得更像是语法糖,让koa用户使用起来更方便。比如下面这段代码://this.request是request.js暴露的对象,它的引用保存在context.request中//用户可以直接通过ctx访问对应的属性。attributenameconstrequest=(context.request=Object.create(this.request));//这个req是请求信息,通过http.createServercontext.req=request.req=response传递给回调函数。请求=请求;读到这里,虽然可以说明ctx.headers是ctx.request.headers的语法糖的问题。但是感觉很奇怪。在这个例子中,ctx.headers访问的是ctx.reqeust上的headers,而不是这个请求信息上的headers。这个请求信息挂在ctx.req上。我们回到reqeust.js的源码,看看headers的getter实现:getheaders(){returnthis.req.headers;}ok,看来这里的this指的是context环境。那么它一定是在application.js的某处更改了this的指向。果然在application.js的构造函数中可以看到:this.request=Object.create(request);应用程序实例上的请求被传递给context.request,而this自然指向上下文。可以看出,koa为了方便开发者使用,在context上做了很多工作。中间件机制中间件的设计是koa最重要的部分。该实现使用koa-compose库连接中间件,形成“洋葱模型”。关于这个库,会在第二篇koa核心库介绍中讲解。应用中处理中间件的函数是use和handleRequest:use函数:传入async/await函数,放到应用实例上的中间件数组中。如果输入是生成器,将调用koa-conver库将其转换为async/await函数。handleRequest(ctx,fnMiddleware)函数:传入的fnMiddleware是串接好的中间件。该函数的作用是添加一个返回给客户端的函数和一个错误处理函数。返回给客户端的函数其实就是respond函数,通过调用res.end()向客户端返回信息,整个过程就结束了。一系列专注于前端和算法的干货分享,欢迎关注(???)