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

超轻量级web框架koa源码阅读

时间:2023-04-03 18:04:50 Node.js

koa是一个非常轻量级的web框架,除了ctx和中间件什么都没有,连最基本的路由器功能都需要安装其他中间件才能实现。但是它虽然简单,但是功能非常强大,仅仅依靠中间件机制就可以构建一个完整的web服务。koa的源码也非常简洁,基本代码不到2000行,非常适合阅读和学习。koa的源码直接从github上获取。本文使用最新版本2.5.1。代码结构当我第一次看到koa的源代码的时候,真的是一头雾水。经过反复确认,确信koa的源码只有四个文件——application.js、context.js、request.js、response.js,位于项目的lib文件夹下。并且基本上看文件名就可以猜出每个文件是干什么的,接下来就是打开查看里面的内容了。koa的基本启动流程首先看package.json中的main。可以知道application.js是一个入口文件,它是一个Application类,继承自event模块下的Emitter类。这里定义了我们使用koa时创建的app实例。分析一个类,首先要看它的构造函数。重点是定义了一个数组中间件,三个属性context、request、response分别是三个对象,这三个对象在对应的另外三个文件中。中定义。这里先不看其他文件。想想我们在使用koa的时候,创建了一个app实例之后,接下来就是使用各种中间件,所以直接看使用方法。use接收一个中间件函数作为参数,首先进行类型验证。如果输入是生成器,在koa2中会先通过convert进行转换(这是为了兼容koa1,后续版本会去掉),最后它只做一件事,就是把这个函数push到中间件数组中。use方法最终会返回this,也就是koa实例本身,也就是说我们可以实现链式调用。设置好中间件后,我们启动koa服务的最后一步就是调用listen方法设置监听端口。接下来我们看一下listen方法的实现。我们会发现listen更简单,只有两行。事实上,没有做任何额外的事情。它只是调用node原生http模块下的createServer方法创建服务,listen方法设置监听,仅此而已。我们都知道http的createServer需要传入一个函数。这个函数是通过调用koa中的回调方法返回的。接下来我们看一下回调的实现。在回调中,首先使用compose将所有的中间件变成一个函数(后面也会详细分析compose的实现)。这里会先调用Emitter中的listenerCount方法判断error事件是否有监听器。如果不是,它将被注册为错误事件。默认的事件监听方法是onerror,然后就是定义我们要传入createServer的函数。该函数接收req和res两个参数,之后koa会对其进行处理:通过调用createContext方法将req和res封装成一个熟悉的ctx对象(createContext的具体工作后面会讲),然后将ctx和之前的处理过的中间件函数fnMiddleware被传递到handleRequest方法中。在handleRequest中,先取出res,先设置status为404,然后在执行中间件后注册成功和失败状态的方法,失败则调用ctx.onerror捕获异常,调用respond方法成功处理结果。这里仍然使用onFinished模块。onFinished可以保证流在关闭、完成、报错时执行相应的回调函数。这里传入我们的异常处理函数来处理错误信息。respond方法,里面做的就是读取ctx信息,将数据写入res,响应请求。至此,整个过程就完成了。ctx创建的createContext中的代码其实很简单,就是创建context、request、response三个对象,然后在context对象上使用ctx的时候挂上各种东西,这样我们就可以在ctx、res和等等各种信息。在创建上下文、请求和响应对象时,使用了当前应用程序类中的三个对象。它们是通过从三个外部文件中导入对象创建的,那么让我们来看看这三个文件中有什么。这三个文件导出所有对象。上下文中只定义了一些基本方法,其余的属性方法全部用于通过delegate代理访问request和response属性。我们已经知道context上面的request和response是通过另外两个文件中的对象创建的。这两个文件的内容比较简洁。它们是我们在使用它们时通常访问的属性和方法。上面的req和res通过getter和setter来控制,实现对实际请求和响应操作的封装。这样整个koacore的四个文件就全部完成了。compose实现原理和中间件机制首先做一些合法性检查。关键是最后的返回结果是一个函数。这个函数就是我们上面的fnMiddleware。它还有两个参数,context和next,并使用index变量记录在它里面。当前处理了哪个中间件,然后从第一个开始调用dispatch方法。首先会判断当前传入的参数与索引的关系。如果在一个中间件中多次调用next,参数会小于index,此时会报错。之后从数组中取出当前的中间件,每次执行都会传入ctx和next,在next中调用dispatch,参数为下一个位置,这样中间件会按顺序加入,最后当i等于中间件数组的长度时,即没有其他中间件,则执行一开始传入的下一个参数。如果fn不存在,返回一个空的承诺。当center执行完毕,也就是前面的中间件的next执行完,自然会触发await向下执行,然后执行权倒序返回。组合的最终结果是先由外向内,再由内向外,也就是我们熟悉的洋葱圈模型。错误处理机制koa的错误处理机制也很有特点。我们只要监听koa实例的error事件,就可以统一处理所有的错误。前面我们提到,调用fnMiddleware失败后,会被统一的onerror方法捕获。该方法对应ctx上的onerror方法。我们来看看里面的实现。其中非常重要的一行是this.app.emit('error',err,this);,因为我们的koa继承自event,所以可以派发一个error事件,我们只需要处理这个事件即可。在前面的中间件处理中,如果出现错误,就会reject,自然可以被catch捕获。以上就是koa的基本核心模块的流程。原理很简单,但是配合各种中间件,koa完全可以实现一个功能齐全的web服务器。本文为原创,愿意分享,转载请提前告知。更多文章,请查看我的主页。感谢您阅读。如果我错了,请纠正我。