koa2的思想就是把一个个用于处理用户请求的中间件串联起来,并支持异步中间件。下面是一个使用koa2的简单示例
const Koa = require('koa')
const app = new Koa()
app.use(async (ctx, next) => {
console.log('a1')
await next()
console.log('a2')
})
app.use(async (ctx, next) => {
console.log('b1')
await next()
console.log('b2')
})
app.use(async ctx => {
console.log('c1')
})
app.listen(8100);
打开浏览器访问8100端口,最终的输出结果为:
从结果可以看出,koa2执行中间件的顺是:先一层层的执行异步中间件,执行到最后一个中间件时,在一层层返回。
上述示例中的async、await是执行异步组件的语法糖,如果读者不熟悉,可以去看下阮一峰老师的文章async函数。
下面让我们来开始对Koa2进行简单的实现。
首先因为使用koa时是用new Koa()的方式,所以Koa肯定是一个类。并且在调用app.use时,需要保存传递进来的函数,所以可以想到内部有一个数组来维护这些函数。下面就是Koa的声明:
class Koa {
constructor() {
this.middlewares = [];
}
use(...middlewares) {
this.middlewares = this.middlewares.concat(middlewares);
}
}
这里通过this.middlewares来维护传递进来的中间件函数,其中use支持传入多个中间件函数。
第二,koa2可以通过app.listen(8100)来监听网络请求,下面就是对listen函数的实现
const http = require('http');
class Koa {
constructor() {
this.middlewares = [];
}
use(...middlewares) {
this.middlewares = this.middlewares.concat(middlewares);
}
listen(...args) {
const server = http.createServer();
server.listen(...args);
}
}
可以看出,其实listen内部就是对http的使用。
下一步就是要实现如何能按照正确的顺序执行异步中间件了。
const compose = (middlewares) => {
return (ctx) => {
const dispatch = (i) => {
const middleware = middlewares[i];
try {
return Promise.resolve(
middleware(ctx, dispatch.bind(null, i + 1))
);
} catch (err) {
return Promise.reject(err);
}
};
dispatch(0);
};
};
这里我们用到一个compose函数来组合中间件,这个方法接受Koa内部维护的middlewares队列,返回一个可以保证执行顺序的函数,它接受ctx作为入参。返回函数内部的dispatch就是中间件函数里面的next。dispatch函数内部的dispatch.bind(null, i+1)保证了中间件的顺序。在middleware(ctx, dispatch.bind(null, i+1))调用时包裹一层Promise.resolve是为了保证即使用户在调用app.use()时传入的不是async函数,也能是Promise的函数。
最后我们来使用下compose函数
const http = require('http');
const quertystring = require('quertystring');
class Koa {
constructor() {
this.middlewares = [];
}
use(...middlewares) {
this.middlewares = this.middlewares.concat(middlewares);
}
createContext(req, res) {
return {
req,
res,
query: quertystring.parse(url.split('?')[1]),
path: req.url.split('?')[0]
};
}
callback() {
const next = compose(this.middlewares);
return (req, res) => {
// 这里简单的组装了一下req和res
const context = this.createContext(req, res);
next(context);
};
}
listen(...args) {
const server = http.createServer(this.callback());
server.listen(...args);
}
}
到此,简单的Koa2就已经实现了。