阅读 Koa 源码
之前阅读了 co
的源码,其实一开始就是想看一下 koa
的源码,然后 koa
又是基于 co
构建的,所以先读了一下 co
,这次再来记录一下 koa
源码中的东西。
compose
compose
方法返回一个 Generator
函数,将所有 middleware
串联起来。
首先, 判断 next
是否为空,如果为空则将 next
赋值为 noop
(空函数)。
接着逆序遍历 middleware
,把 middleware
数组中的后一个产生的 iterator
作为前一个 middleware
的参数。
最后,yield *
第一个 next
。
理解这个函数,就明白在中间件中 yield next
是怎么工作的了,因为数组中后一个会把产生的 iterator
(也就是 next
)传给前一个。
Application
构造函数
先来看看 Application
的构造函数,注意到 middleware
, context
,request
和 response
。Context
,Request
和 Response
都是 Koa
下面自定义的三个类。
Application#use
这个函数就是检查 fn
的合法性,然后把 fn
添加到 middleware
数组里面,返回 this
可以让这个函数链式调用。
Application#callback
调用 callback
函数返回的是一个形似 function(res, req) {}
的函数。这个返回的函数传给 http.createServer
。
让我们仔细看一下 callback
,首先,调用 compose
将 middleware
变为一个 Generator
函数,然后使用 co.wrap
将其变为一个返回 Promise
的函数。关于 co
的讨论,可以看我写的另一篇文章 阅读 co 源码。
接着保存 this
给 self
。
注意到 this.listeners('error').length
这段代码,Application.prototype
的原型是 EventEmitter.prototype
,因此拥有这个属性。
接着就是返回这个回调函数了,onFinished
是在一个 HTTP 请求关闭,结束或者出现错误的时候调用的,这里我们传入了 ctx.onerror
,createContext
所做的就是创建一个 Context
对象,然后创建 Request
和 Response
对象,并将它们进行一系列的关联。
可以看到下面还有一个 respond.call(ctx)
,respond
是一个关于 Response
的帮助函数,最终结束掉响应。
Context
Context
类(准确来说是用作一个原型)作为一个上下文,提供了到 Response
对象和 Request
对象的代理。
Request
提供了一系列与请求相关的方法。
Response
提供了一系列与响应相关的方法。
总结
最后附上一个 Koa
调用的流程
Koa
是一个非常轻量级的 Web 框架,连路由都没有提供,去看它的源码的时候,发现居然只有4个文件,每个文件代码都不多,并且有许多注释,比较容易阅读。正是这样一个轻量级的框架,配合大量的中间件,可以构建出一个功能强大的应用,这也许就是 Koa 的魅力所在吧。