这段代码还有个点需要注意,那就是Application继承自Node.js原生的EventEmitter类,这个类其实就是一个发布订阅模式,可以订阅和发布消息,我在另一篇文章里面详细讲过他的源码。所以他有些方法如果在application.js里面找不到,那可能就是继承自EventEmitter,比如下图这行代码:
这里有this.on这个方法,看起来他应该是Application的一个实例方法,但是这个文件里面没有,其实他就是继承自EventEmitter,是用来给error这个事件添加回调函数的。这行代码if里面的this.listenerCount也是EventEmitter的一个实例方法。
Application类完全是JS面向对象的运用,如果你对JS面向对象还不是很熟悉,可以先看看这篇文章:https://juejin.im/post/6844904069887164423。
app.use从我们前面的使用示例可以看出app.use的作用就是添加一个中间件,我们在构造函数里面也初始化了一个变量middleware,用来存储中间件,所以app.use的代码就很简单了,将接收到的中间件塞到这个数组就行:
use(fn) { // 中间件必须是一个函数,不然就报错 if (typeof fn !== "function") throw new TypeError("middleware must be a function!"); // 处理逻辑很简单,将接收到的中间件塞入到middleware数组就行 this.middleware.push(fn); return this; }注意app.use方法最后返回了this,这个有点意思,为什么要返回this呢?:类的实例方法返回this可以实现链式调用。比如这里的app.use就可以连续点点点了,像这样:
app.use(middlewaer1).use(middlewaer2).use(middlewaer3)为什么会有这种效果呢?因为这里的this其实就是当前实例,也就是app,所以app.use()的返回值就是app,app上有个实例方法use,所以可以继续点app.use().use()。
app.use的官方源码看这里:
app.listen在前面的示例中,app.listen的作用是用来启动服务器,看过前面用原生API实现web服务器的朋友都知道,要启动服务器需要调用原生的http.createServer,所以这个方法就是用来调用http.createServer的。
listen(...args) { const server = http.createServer(this.callback()); return server.listen(...args); }这个方法本身其实没有太多可说的,只是调用http模块启动服务而已,主要的逻辑都在this.callback()里面了。
app.listen的官方源码看这里:
app.callbackthis.callback()是传给http.createServer的回调函数,也是一个实例函数,这个函数必须符合http.createServer的参数形式,也就是
http.createServer(function(req, res){})所以this.callback()的返回值必须是一个函数,而且是这种形式function(req, res){}。
除了形式必须符合外,this.callback()具体要干什么呢?他是http模块的回调函数,所以他必须处理所有的网络请求,所有处理逻辑都必须在这个方法里面。但是Koa的处理逻辑是以中间件的形式存在的,对于一个请求来说,他必须一个一个的穿过所有的中间件,具体穿过的逻辑,你当然可以遍历middleware这个数组,将里面的方法一个一个拿出来处理,当然也可以用业界更常用的方法:compose。
compose一般来说就是将一系列方法合并成一个方法来方便调用,具体实现的形式并不是固定的,有面试中常见的用reduce实现的compose,也有像Koa这样根据自己需求单独实现的compose。Koa的compose也单独封装了一个库koa-compose,这个库源码也是我们必须要看的,我们一步一步来,先把this.callback写出来吧。
callback() { // compose来自koa-compose库,就是将中间件合并成一个函数 // 我们需要自己实现 const fn = compose(this.middleware); // callback返回值必须符合http.createServer参数形式 // 即 (req, res) => {} const handleRequest = (req, res) => { const ctx = this.createContext(req, res); return this.handleRequest(ctx, fn); }; return handleRequest; }这个方法先用koa-compose将中间件都合成了一个函数fn,然后在http.createServer的回调里面使用req和res创建了一个Koa常用的上下文ctx,然后再调用this.handleRequest来真正处理网络请求。注意这里的this.handleRequest是个实例方法,和当前方法里面的局部变量handleRequest并不是一个东西。这几个方法我们一个一个来看下。
this.callback对应的官方源码看这里:
koa-composekoa-compose虽然被作为了一个单独的库,但是他的作用却很关键,所以我们也来看看他的源码吧。koa-compose的作用是将一个中间件组成的数组合并成一个方法以便外部调用。我们先来回顾下一个Koa中间件的结构:
function middleware(ctx, next) {}这个数组就是有很多这样的中间件:
[ function middleware1(ctx, next) {}, function middleware2(ctx, next) {} ]