# 入门

# 创建 koa2 工程

# 项目搭建

首先,我们创建一个目录 hello-koa 并作为工程目录用 VS Code 打开。然后

npm init
1

# 添加依赖

npm install koa

或者

yarn add koa

1
2
3
4
5
6

# 创建第一个例子

我们创建 app.js,输入以下代码:

// 导入koa,和koa 1.x不同,在koa2中,我们导入的是一个class,因此用大写的Koa表示:
const Koa = require("koa");

// 创建一个Koa对象表示web app本身:
const app = new Koa();

// 对于任何请求,app将调用该异步函数处理请求:
app.use(async (ctx, next) => {
  await next();
  ctx.response.type = "text/html";
  ctx.response.body = "<h1>Hello, koa2!</h1>";
});

// 在端口3000监听:
app.listen(3000);
console.log("app started at port 3000...");
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

对于每一个 http 请求,koa 将调用我们传入的异步函数来处理:

async (ctx, next) => {
  await next();
  // 设置response的Content-Type:
  ctx.response.type = "text/html";
  // 设置response的内容:
  ctx.response.body = "<h1>Hello, koa2!</h1>";
};
1
2
3
4
5
6
7

其中,参数 ctx 是由 koa 传入的封装了 request 和 response 的变量,我们可以通过它访问 request 和 response,next 是 koa 传入的将要处理的下一个异步函数。

上面的异步函数中,我们首先用 await next();处理下一个异步函数,然后,设置 response 的 Content-Type 和内容。

由 async 标记的函数称为异步函数,在异步函数中,可以用 await 调用另一个异步函数,这两个关键字将在 ES7 中引入。

# 启动例子

我们在 VS Code 中执行 app.js,调试控制台输出如下:

node ./app.js
1

我们打开浏览器,输入http://localhost:3000,即可看到效果:

还可以直接用命令 node app.js 在命令行启动程序,或者用 npm start 启动。npm start 命令会让 npm 执行定义在 package.json 文件中的 start 对应命令:

"scripts": {
    "start": "node app.js"
}
1
2
3

启动执行:

npm run start
1

# middleware

让我们再仔细看看 koa 的执行逻辑。核心代码是:

app.use(async (ctx, next) => {
  await next();
  ctx.response.type = "text/html";
  ctx.response.body = "<h1>Hello, koa2!</h1>";
});
1
2
3
4
5

每收到一个 http 请求,koa 就会调用通过 app.use()注册的 async 函数,并传入 ctx 和 next 参数。

我们可以对 ctx 操作,并设置返回内容。但是为什么要调用 await next()?

原因是 koa 把很多 async 函数组成一个处理链,每个 async 函数都可以做一些自己的事情,然后用 await next()来调用下一个 async 函数。我们把每个 async 函数称为 middleware,这些 middleware 可以组合起来,完成很多有用的功能。

例如,可以用以下 3 个 middleware 组成处理链,依次打印日志,记录处理时间,输出 HTML:

app.use(async (ctx, next) => {
  console.log(`${ctx.request.method} ${ctx.request.url}`); // 打印URL
  await next(); // 调用下一个middleware
});

app.use(async (ctx, next) => {
  const start = new Date().getTime(); // 当前时间
  await next(); // 调用下一个middleware
  const ms = new Date().getTime() - start; // 耗费时间
  console.log(`Time: ${ms}ms`); // 打印耗费时间
});

app.use(async (ctx, next) => {
  await next();
  ctx.response.type = "text/html";
  ctx.response.body = "<h1>Hello, koa2!</h1>";
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

middleware 的顺序很重要,也就是调用 app.use()的顺序决定了 middleware 的顺序。

此外,如果一个 middleware 没有调用 await next(),会怎么办?答案是后续的 middleware 将不再执行了。这种情况也很常见,例如,一个检测用户权限的 middleware 可以决定是否继续处理请求,还是直接返回 403 错误

app.use(async (ctx, next) => {
    if (await checkUserPermission(ctx)) {
        await next();
    } else {
        ctx.response.status = 403;
    }
});
1
2
3
4
5
6
7

理解了 middleware,我们就已经会用 koa 了!

最后注意 ctx 对象有一些简写的方法,例如 ctx.url 相当于 ctx.request.url,ctx.type 相当于 ctx.response.type。