
1.1.3 Koa入门
Koa v2的核心是使用面向更新特性的async函数的中间件,其正式版本发布前后性能略有不同。
○ 正式版本发布前:支持3种中间件写法(Promise、Generator、async函数)。
○ 正式版本发布后:推荐使用ES2017的async函数作为中间件。
从发布前支持3种中间件写法,我们可以看到Koa v2对通用性的思考是非常深刻的。在正式版本发布之后,Koa小组直接将GitHub上的说明改为“Expressive middleware for Node.js using ES2017 async functions”,这是一个非常明确的态度,表明Koa的定位为下一代Web开发框架,将一如既往地支持新特性。我们在《狼叔(卷1):更了不起的Node.js》中介绍过异步流程控制的演进过程,可以说async函数确实是目前最好的异步流程控制解决方案,所以Koa和async函数的结合近乎完美。
➘ 你好,Koa
让我们来看看最“潮”的Koa v2(基于async函数的中间件)的基本写法,代码如下。

接着,在app.js里键入如下代码。

执行前一定要确保你的Node.js是v7.6以上的,否则无法直接执行带有async函数的代码。

下面对Koa代码中的要点进行说明。
○ 从表面上看,中间件有变化,之前支持ES6 Generator,现在支持async函数+箭头函数,即async (ctx,next)=>{},这样能使语义更清晰。
○ 参数有变化,之前只有next参数,现在又多了一个上下文ctx参数。在之前的ES6 Generator中间件里只能使用隐式的this作为上下文,而在async函数中可以使用显式的ctx作为上下文,更加友好。
访问http://127.0.0.1:3000/,控制台中日志输出如下。

从日志中我们可以看出代码执行规律,具体如下。
○ 先经过logger中间件:[logger middleware] before await...。
○ 然后跳转到[response middleware] response...。
○ 最后执行[logger middleware] after await...。
这3处日志输出很明显是在logger中间件的await next()代码前后进行处理的,通过await next()将执行权转交给下一个中间件,即response中间件,于是代码就借助async函数变成顺序执行了,这就是Koa v2带给我们的最直观的变化。
➘ 为什么Koa v2中要新增一个ctx参数
Koa v2中新增一个ctx参数的最主要原因是,在编写Koa App时使用了async箭头函数,示例如下。

在这种情况下,使用this是万万不能的。因为箭头函数是定义时绑定的,this指向箭头函数外部,而不是运行时的对象。
➘ Koa v1和Koa v2的用法差异
在Koa v1里,初始化app对象的方式和Express是一样的,具体如下。

在Koa v2里,我们使用面向对象的写法,需要对Koa类进行实例化后才能获得app对象,具体如下。

下面我们从源码层面进行对比,找出Koa v1和Koa v2在用法上的差异。
1.Koa v1示例代码

2.Koa v2示例代码

通过上面的源码对比,很明显可以看出,Koa v1对外导出的是函数,而Koa v2对外导出的是类,需要通过new这个关键字来进行实例化。另外,值得注意的是,在Koa v2源码里,只有Application进行了类化,其他的仍保持之前的风格,这大概是因为还没有必须修改的必要。