跳到主要内容

Koa

简介

Koa 是一个基于 Node.js 的 Web 框架,致力于成为 web 应用和 API 开发领域中的一个更小、更富有表现力、更健壮的基石。

  • 通过异步函数,可以避免回调函数嵌套
  • koa 的核心方法中没有绑定任何中间件

安装:

npm install koa

示例:

const Koa = require('koa');
const app = new Koa();

app.use(async ctx => {
ctx.body = 'Hello World';
});

app.listen(3000);

中间件

Koa 是一个中间件框架,中间件就是一个函数,它接受两个参数:ctx、next

  • ctx 是一个请求的上下文(context)
  • next 是调用执行下游中间件的函数。在代码执行完成后通过 then 方法返回一个 Promise

更多中间件

可以采用两种不同的方法来实现中间件:

  • async function(推荐)
  • common function

以下是使用两种不同方法实现一个日志中间件的示例:

1、async function

app.use(async (ctx, next) => {
const start = Date.now();
await next();
const ms = Date.now() - start;
console.log(`${ctx.method} ${ctx.url} - ${ms}ms`);
});

2、Common function

app.use((ctx, next) => {
const start = Date.now();
return next().then(() => {
const ms = Date.now() - start;
console.log(`${ctx.method} ${ctx.url} - ${ms}ms`);
});
});

上下文、请求响应

每个中间件都接收一个 Koa 的 Context 对象,该对象封装了一个传入的 http 消息,并对该消息进行了相应的响应。

app.use(async (ctx, next) => {
await next();
});
  • Koa 提供了一个 Request 对象作为 Context 的 request 属性。 Request 对象提供了用于处理 http 请求的方法,该请求委托给 Node http 模块的 IncomingMessage
  • Koa 提供了一个 Response 对象作为 Context 的 response 属性。 Response 对象提供了用于处理 http 响应的方法,该响应委托给ServerResponse

Koa 对 Node 的请求和响应对象进行委托而不是扩展它们。这种模式提供了更清晰的接口,并减少了不同中间件与 Node 本身之间的冲突,并为流处理提供了更好的支持。

  • IncomingMessage 可以作为 Context 上的 req 属性被直接访问
  • ServerResponse 可以作为 Context 上的 res 属性被直接访问

例 1:检查请求客户端 xml 支持

app.use(async (ctx, next) => {
ctx.assert(ctx.request.accepts('xml'), 406);
// 相当于:
// if (!ctx.request.accepts('xml')) ctx.throw(406);
await next();
});

例 2:使用 Koa 的 Response 对象将文件作为响应体流式传输

app.use(async (ctx, next) => {
await next();
ctx.response.type = 'xml';
ctx.response.body = fs.createReadStream('really_large.xml');
});

Context 对象还提供了其 request 和 response 方法的快捷方式:

  • ctx.type 可以代替 ctx.response.type
  • ctx.accepts 可以代替 ctx.request.accepts

洋葱模型

当中间件调用next()时,该函数会挂起并将控制权传递给下一个中间件。当下游不再有中间件执行时,会继续执行上游的中间件。

const Koa = require('koa');
const app = new Koa();

app.use(async (ctx, next) => {
console.log(1);
next();
console.log(2);
});

app.use(async (ctx, next) => {
console.log(3);
next();
console.log(4);
});

app.use(async (ctx, next) => {
console.log(5);
next();
console.log(6);
});

app.listen(3000);

打印结果是:1 3 5 6 4 2

路由

npm install @koa/router
const Koa = require('koa');
const Router = require('@koa/router');

const app = new Koa();
const router = new Router();

router.get('/', (ctx, next) => {
console.log(ctx);
});

router.post('/list', ctx => {
ctx.body = ['k1', 'k2', 'k3'];
});

app.use(router.routes()).use(router.allowedMethods());

app.listen(3000);

静态资源

使用 koa-static

npm install koa-static
const Koa = require('koa');
const path = require('path');
const static = require('koa-static');

const app = new Koa();

app.use(static(path.join(__dirname, 'public')));

app.use(async ctx => {
ctx.body = 'hello world';
});

app.listen(3000);

Koa1 和 Koa2 的区别

处理异步的方式:Koa1 采用 generator函数,yield next 进入下一个中间件。Koa2 采用 async/await,使用 await next()进入下一个中间件

Koa 与 Express 比较

异步流程控制

express 采用 callback 回调函数来处理异步, koa1 采用 generator,koa2 采用 async/await

使用同步的写法来处理异步,明显好于 callback

更轻量

koa 不提供内置的中间件,如路由、日志、视图模板等,需要自己搭配。而 express 是大而全的,内置了很多中间件

Context 对象

koa 增加了一个 Context 对象,作为这次请求的上下文对象(在 koa2 中作为中间件的第一个参数传入)。同时 Context 上也挂载了 Request 和 Response 两个对象。和 Express 类似,这两个对象都提供了大量的方法辅助开发, 在保存一些公有的参数时变得更加合情合理

中间件模型

express 基于 Connect 中间件,线性模型

koa 中间件采用洋葱模型(对于每个中间件,在完成了一些事情后,可以优雅的将控制权传递给下一个中间件,并能够等待它完成,当后续的中间件完成处理后,控制权又回到了自己这里)

洋葱模型的实现原理