本文是koa快速入门的第一篇:基础使用,后续还会再写两篇文章,介绍「koa项目最佳实践」和「深究koa原理」。
koa是Express团队打造的新一代web框架,特点是更小,更舒服的开发体验。
koa对Express的改进之一,是对异步实现方式的改进。
Express是基于ES5的,其异步写法更常见的是基于回调,当然只要node版本支持,也可以用async/await,但是其自身对async/await没有太多支持,如果用了async/await,在错误处理和中间件执行顺序上要开发者自己解决一些问题。
下面代码是Express使用Promise回调时候的一个异常处理示例。
const express = require("express");
const app = express();
//create a server object:
app.get("/", (req, res, next) => {
// do some sync stuff
queryDb()
.then((data) => {
res.write("Hello" + data); //write a response to the client
res.end();
}) // handle data
.catch(next); //catch(next)处理异步代码块中的任何异常
});
app.use((err, req, res, next) => {
res.write("reject" + err); //write a response to the client
res.end();
});
// 所有异步代码必须返回 Promise
const queryDb = function () {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject("hi");
}, 1000);
});
};
app.listen(8080, function () {
console.log("server running on 8080");
}); //the server object listens on port 8080
koa 1.0基于ES6编写,使用generator实现异步,代码看起来是下面这样,已经实现了同步写法写异步。
var koa = require('koa');
var app = koa();
app.use('/test', function *() {
yield doReadFile1();
var data = yield doReadFile2();
this.body = data;
});
app.listen(3000);
koa 2.0基于ES7编写,ES7引入了async/await关键字,使得同步写异步更加方便,于是koa2的异步代码看起来是这样的。
app.use(async (ctx, next) => {
await next();
var data = await doReadFile();
ctx.response.type = 'text/plain';
ctx.response.body = data;
});
本文的koa都是指koa 2.0版本。
koa和express另一个区别在于,koa本身不包含任何的中间件,只是一个中间件框架,具体功能都是由各种外部的中间件实现的,而express自带了路由、模板、发送文件、jsonp等等功能,这也使koa框架自身更小更简洁。
hello world😄
const Koa = require('koa');
const app = new Koa();
app.use(async ctx => {
ctx.body = 'Hello World';
});
app.listen(3000);
app.listen是http.createServer的语法糖。
const Koa = require('koa');
const app = new Koa();
app.listen(3000);
等于
const http = require('http');
const Koa = require('koa');
const app = new Koa();
http.createServer(app.callback()).listen(3000);
其中app.callback用于处理成http.createServer适用的回调方法。
app.use用于把中间件添加到应用程序中,中间件的执行顺序是洋葱模型,这个我们在「深究原理」这一趴再详细说说。
适用方法参考下面这个代码。
const Koa = require('koa');
const app = new Koa();
// x-response-time
app.use(async (ctx, next) => {
const start = Date.now();
await next();
const ms = Date.now() - start;
ctx.set('X-Response-Time', `${ms}ms`);
});
// logger
app.use(async (ctx, next) => {
const start = Date.now();
await next();
const ms = Date.now() - start;
console.log(`${ctx.method} ${ctx.url} - ${ms}`);
});
// response
app.use(async ctx => {
ctx.body = 'Hello World';
});
app.listen(3000);
「前言」那里,我们看到express的错误处理,其实并不太优雅。
相比之下,koa的错误处理好得多,koa通过添加一个“error”监听器来集中进行错误处理。
app.on('error', (err, ctx) => {
log.error('server error', err, ctx)
});
koa-router用于管理url路径
const Koa = require('koa');
const Router = require('@koa/router');
const app = new Koa();
const router = new Router();
router.get('/', (ctx, next) => {
// ctx.router available
});
router.post('/signin', (ctx, next) => {
// ctx.router available
});
app
.use(router.routes())
.use(router.allowedMethods()); // allowedMethods用于在响应头返回允许的请求方式
更高级的操作,参考koa-router
koa-body用于解析body,支持multipart
, urlencoded
, and json
格式的请求body。
const Koa = require('koa');
const app = new Koa();
const router = require('koa-router')();
const koaBody = require('koa-body');
router.post('/users', koaBody(),
(ctx) => {
console.log(ctx.request.body);
// => POST body
ctx.body = JSON.stringify(ctx.request.body);
}
);
app.use(router.routes());
app.listen(3000);
console.log('curl -i http://localhost:3000/users -d "name=test"');
更高级的操作,参考koa-body
前面我们都是在介绍在单文件里面使用koa,但是这并不优美,更好的实现方式是把路由(route)、逻辑(controller)等等按功能分块。
遗憾的是,不像express,koa没有官方的生成器,可以按模版生成koa的项目。但是官方提供了一些其他人的最佳实践,可以参考下koa项目示例。
作者看了一圈没有找到合心意的模板,于是决定自己实现一下,明天我们会更新一篇文章,介绍一下作者心目中的koa最佳实践(挖坑🕳️),敬请期待😄。
koa源码实现和洋葱模型的的实现方式,后天我们再继续更文介绍(挖坑🕳️),敬请期待😄