koa入门

创建koa2工程

首先,我们创建一个目录hello-koa并作为工程目录用VS Code打开。然后,我们创建app.js,输入以下代码:

  1. // 导入koa,和koa 1.x不同,在koa2中,我们导入的是一个class,因此用大写的Koa表示:
  2. const Koa = require('koa');
  3. // 创建一个Koa对象表示web app本身:
  4. const app = new Koa();
  5. // 对于任何请求,app将调用该异步函数处理请求:
  6. app.use(async (ctx, next) => {
  7. await next();
  8. ctx.response.type = 'text/html';
  9. ctx.response.body = '<h1>Hello, koa2!</h1>';
  10. });
  11. // 在端口3000监听:
  12. app.listen(3000);
  13. console.log('app started at port 3000...');

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

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

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

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

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

现在我们遇到第一个问题:koa这个包怎么装,app.js才能正常导入它?

方法一:可以用npm命令直接安装koa。先打开命令提示符,务必把当前目录切换到hello-koa这个目录,然后执行命令:

  1. C:\...\hello-koa> npm install koa@2.0.0

npm会把koa2以及koa2依赖的所有包全部安装到当前目录的node_modules目录下。

方法二:在hello-koa这个目录下创建一个package.json,这个文件描述了我们的hello-koa工程会用到哪些包。完整的文件内容如下:

  1. {
  2. "name": "hello-koa2",
  3. "version": "1.0.0",
  4. "description": "Hello Koa 2 example with async",
  5. "main": "app.js",
  6. "scripts": {
  7. "start": "node app.js"
  8. },
  9. "keywords": [
  10. "koa",
  11. "async"
  12. ],
  13. "author": "Michael Liao",
  14. "license": "Apache-2.0",
  15. "repository": {
  16. "type": "git",
  17. "url": "https://github.com/michaelliao/learn-javascript.git"
  18. },
  19. "dependencies": {
  20. "koa": "2.0.0"
  21. }
  22. }

其中,dependencies描述了我们的工程依赖的包以及版本号。其他字段均用来描述项目信息,可任意填写。

然后,我们在hello-koa目录下执行npm install就可以把所需包以及依赖包一次性全部装好:

  1. C:\...\hello-koa> npm install

很显然,第二个方法更靠谱,因为我们只要在package.json正确设置了依赖,npm就会把所有用到的包都装好。

注意,任何时候都可以直接删除整个node_modules目录,因为用npm install命令可以完整地重新下载所有依赖。并且,这个目录不应该被放入版本控制中。

现在,我们的工程结构如下:

  1. hello-koa/
  2. |
  3. +- .vscode/
  4. | |
  5. | +- launch.json <-- VSCode 配置文件
  6. |
  7. +- app.js <-- 使用koajs
  8. |
  9. +- package.json <-- 项目描述文件
  10. |
  11. +- node_modules/ <-- npm安装的所有依赖包

紧接着,我们在package.json中添加依赖包:

  1. "dependencies": {
  2. "koa": "2.0.0"
  3. }

然后使用npm install命令安装后,在VS Code中执行app.js,调试控制台输出如下:

  1. node --debug-brk=40645 --nolazy app.js
  2. Debugger listening on port 40645
  3. app started at port 3000...

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

koa-browser

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

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

koa middleware

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

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

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

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

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

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

  1. app.use(async (ctx, next) => {
  2. console.log(`${ctx.request.method} ${ctx.request.url}`); // 打印URL
  3. await next(); // 调用下一个middleware
  4. });
  5. app.use(async (ctx, next) => {
  6. const start = new Date().getTime(); // 当前时间
  7. await next(); // 调用下一个middleware
  8. const ms = new Date().getTime() - start; // 耗费时间
  9. console.log(`Time: ${ms}ms`); // 打印耗费时间
  10. });
  11. app.use(async (ctx, next) => {
  12. await next();
  13. ctx.response.type = 'text/html';
  14. ctx.response.body = '<h1>Hello, koa2!</h1>';
  15. });

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

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

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

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

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

参考源码

hello-koa

读后有收获可以支付宝请作者喝咖啡,读后有疑问请加微信群讨论:

koa入门 - 图2 koa入门 - 图3