手写Koa.js源码 (2)

这段代码还有个点需要注意,那就是Application继承自Node.js原生的EventEmitter类,这个类其实就是一个发布订阅模式,可以订阅和发布消息,我在另一篇文章里面详细讲过他的源码。所以他有些方法如果在application.js里面找不到,那可能就是继承自EventEmitter,比如下图这行代码:

image-20201029151525287

这里有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.callback

this.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-compose

koa-compose虽然被作为了一个单独的库,但是他的作用却很关键,所以我们也来看看他的源码吧。koa-compose的作用是将一个中间件组成的数组合并成一个方法以便外部调用。我们先来回顾下一个Koa中间件的结构:

function middleware(ctx, next) {}

这个数组就是有很多这样的中间件:

[ function middleware1(ctx, next) {}, function middleware2(ctx, next) {} ]

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpxfgd.html