如果你想提升node水平,那么我极力推荐你看看koa源码。
koa作者是神一般的男人TJ Holowaychuk,源码设计巧妙而又短小精悍,既能领略koa的设计思想,而又避免了源码过冗长而带来的疲劳感。
这篇文章,我们从源头出发,一起领略kao的精髓,同时围绕下面三点介绍:
1.老生常谈的 洋葱模型
2. context的 委托模式
3. koa的 错误处理
目录结构
话不多说,让我们开始吧~首先请跟我一起打开命令行 输入
touch koa_learn.js && npm init && npm i koa
然后用任何一个ide打开koa_learn.js,我们看看node_modules下的koa目录结构。
除了readme、历史信息、开源许可证,真正的源码部分只有四个文件。application、context、request、response。 打开package.json文件,我们可以看到koa的入口。
"main": "lib/application.js",
application.js
好的,让我们开始写一个简单的Demo。
const Koa = require("koa");
const app = new Koa();
app.use(async (ctx, next) => {
ctx.body = "你好,YouHe";
});
app.listen(3000);
然后在终端上
curl http://localhost:3000
//终端上打印出 *你好,YouHe*
我们看到,app.js里引入了koa,然后用new来实例化一个app,之后我们使用了app.use传入一个async函数,也就是kao中间件,最后调用app.listen方法,至此一个kao应用就跑起来了。
打开application.js文件,首先看到这里暴露了一个Application类,继承于Emitter(错误处理讲到)。
constructor
module.exports = class Application extends Emitter {
constructor(options) {
super();
options = options || {
}; //配置
this.proxy = options.proxy || false; //是否proxy模式
this.subdomainOffset = options.subdomainOffset || 2; //domain要忽略的偏移量
this.proxyIpHeader = options.proxyIpHeader || 'X-Forwarded-For'; //proxy自定义头部
this.maxIpsCount = options.maxIpsCount || 0; //代理服务器数量
this.env = options.env || process.env.NODE_ENV || 'development'; //环境变量
if (options.keys) this.keys = options.keys; // 自定义cookie 密钥
this.middleware = []; //中间件数组
this.context = Object.create(context);
this.request = Object.create(request);
this.response = Object.create(response);
if (util.inspect.custom) {
//自定义检查,这里的作用是get app时,去执行this.inspect 。感兴趣可见http://nodejs.cn/api/util.html#util_util_inspect_custom
this[util.inspect.custom] = this.inspect;
}
}
...
这里是constructor里做了一些配置,这里主要是
this.middleware = []; //中间件数组
this.context = Object.create(context);
this.request = Object.create(request);
this.response = Object.create(response);
首先是一个中间件数组,我们用use使用的中间件都会放进这个数组中,然后分别用Object.create拷贝的context、request、response,分别对应koa目录的三个文件。这里用Object.create是因为我们在同一个应用中可能会有多个new Koa的app,为了防止这些app相互污染,用拷贝的方法让其引用不指向同一个地址。
app.use
这里首先判断use传进来的参数是不是一个函数,然后判断函数是否为generator函数,并将其转化为generator,这里用到了convert函数,本质上其实是用到co去把generator转成了一个返回promise的function(说着很绕口,可以理解转成类似async函数)
const converted = function (ctx, next) {
return co.call(ctx, mw.call(ctx, createGenerator(next)))
}
其实除了这个个人觉得很鸡肋的DEBUG,(感兴趣试试命令行输入 DEBUG=koa* node app.js),重点在于这个 this.middleware.push(fn);
,它做的只是把中间件放进了middleware数组。
app.listen
之后我们就用到listen方法
listen(...args) {
debug('listen');
const server = http