前面章节已经介绍了源自于 Koa 的中间件和路由设计,前面介绍的静态资源服务器和路由可以通过 koa 的中间件轻松实现,后面章节要介绍的企业级 web 框架 egg.js 正是基于 koa 开发的,在本章的最后简单介绍一下 koa 的使用
koa 是什么
next generation web framework for node.js
Koa 是一个使用 Node.js 的 web 开发框架,其核心是前面章节中反复使用的洋葱模型中间件,通过中间件解决 web 开发中的 http 处理、业务逻辑实现,web 框架需要具备两个最基本的特征
- 提供 web 开发常见的各种基础功能
- 提供一套规范的编码方式,让开发者可以聚焦在业务逻辑实现,代码就可以在框架内按照预期执行
有个说法讲库和框架的区别:You call library, framework calls you.
koa 的 web 处理基础功能通过中间件提供,其开发规范也是建立在洋葱模型中间件基础上,现在使用的是 koa 2.x 版本,在此之前 Node.js 社区 web 框架经过了几个里程碑
Express
Express 是 Node.js 的第一代流行 web 开发框架,主要是对 http 模块进行封装,并提供了路由、模板渲染等 web 开发常用功能,功能齐全但需要全量引用
由于出现早于 Promise,错误处理使用 Node.js 的 callback 风格,相应有了回调地狱问题,不过随着 Promise 流行,现在 Express 4.x、5.x 已经没了这个问题
Express 中间件是线性执行的,每一个中间件处理完成之后只有两个选择
- 交给下一个中间件
- 返回 response
只要是离开中间件后就再也无法返回,这样的设计让逻辑非常简单,但在很多需要多次处理请求的实现上变得复杂:比如统计一个请求的耗时,在 Express 中充斥着大量利用事件或者回调来 hack 这种需求的处理方式
exports.responseTime = function () {
return function (req, res, next) {
req.startTime = new Date(); // 开始时间
const trackTime = function () {
const endTime = new Date(); // 结束时间
const duration = endTime - req.startTime;
console.log('X-Response-Time: ', duration + 'ms');
}
res.once('finish', trackTime);
res.once('close', trackTime);
return next();
}
}
因为其简单、功能高度集成的特性,Express 现在仍然是最流行的 Node.js web 开发框架,但功能高度集成带来的不灵活和中间件的线性调用特性让越来越多企业级 web 框架封装都选择了使用 koa
koa 1.x
Koa 同样出自 Express 团队之手,除了 API 变化,相对于 Express 做了两个最重要的变更
- 不再内置任何中间件,所有 web 处理中间件都需要引用,灵活性和复杂性相伴而来(从这个角度讲 Express 才更像是 web 框架)
- 利用 generator 特性实现洋葱模型中间件,异步处理不再依赖 callback(主要靠 co 模块实现)
异步的代码书写起来更像是同步代码了,但使用 generator 中间件编写风格现在看起来会感觉怪怪的
app.use(function *(next) {
const startTime = new Date();
yield next;
const duration = new Date - startTime;
console.log('X-Response-Time: ', duration + 'ms');
})
koa 2.x
理念和 Koa 1.x 一致,不过推荐异步处理变成了 async/await,其中间件实现也就是前面介绍过的 koa-compose,中间件编写风格好理解了很多
app.use(async (ctx, next) => {
const start = Date.now();
await next();
const ms = Date.now() - start;
console.log(`${ctx.method} ${ctx.url} - ${ms}`);
});
koa 2.x 也把之前使用 this 获取请求、响应等对象修改为了使用 ctx 对象
Hello world
使用 koa 编写 hello world 非常简单
const Koa = require('koa');
const app = new Koa();
// response
app.use(ctx => {
ctx.body = 'Hello Koa';
});
app.listen(3000);
- app.use(middleware):注册中间件
- app.listen(port):和 http 模块功能一样
-
context
context 对象在每个请求中被创建,是 koa 对当前请求的 request、response 对象封装,并提供了很多实用处理工具方法,做为中间件的第一个参数被传入
app.use(async (ctx, next) => {
ctx; // is the Context
ctx.request; // is a koa Request
ctx.response; // is a koa Response
});
context 中用几个常用的属性
ctx.request: koa 封装的 request 对象,中间件应该尽量使用
- ctx.req:Node.js 原生的 request 对象
- ctx.response:koa 封装的 response 对象,中间件应该尽量使用
- ctx.res:Node.js 原生的 response 对象
- ctx.state:koa 推荐的命名空间,用于通过中间件传递信息到前端视图
- ctx.app:对应用实例 app 的引用
- ctx.cookies:cookie 操作对象
ctx.throw:通过 http status 抛出错误,让 koa 可以正确处理
ctx.throw(400);
ctx.throw(400, 'name required');
ctx.throw(400, 'name required', { user: user });
request & response
koa 封装的 request 和 response 对象包含了 Node.js 原生 req、res 的全部属性,并且提供了很多快捷操作,不再一一介绍
- response
response.body
通过设置 response.body 可以设置 http 响应,设置的值可以是以下类型
string
Buffer
Stream
可读流Object
||Array
null
不返回响应静态资源服务器
使用 koa-static 中间件可以轻松实现前面章节介绍的静态资源服务器 ```javascript const path = require(‘path’); const Koa = require(‘koa’); const static = require(‘koa-static’);
const app = new Koa();
const root = path.join(__dirname, ‘./public’); app.use(static(root, {}));
app.listen(3000);
<a name="ehRit"></a>
## Getting start
官方介绍了三个学习 koa 的网站
- [Kick-Off-Koa](https://github.com/koajs/kick-off-koa) - An intro to Koa via a set of self-guided workshops.
- [Workshop](https://github.com/koajs/workshop) - A workshop to learn the basics of Koa, Express' spiritual successor.
- [Introduction Screencast](https://knowthen.com/episode-3-koajs-quickstart-guide/) - An introduction to installing and getting started with Koa
其实只要了解了 Node.js 的 http 模块和 koa 的洋葱模型中间件,koa 非常好理解,但实现一个完整的 web 网站需要整合大量的中间件( koa 核心维护者[死马](https://github.com/dead-horse)整理了一些常用的 [https://www.npmjs.com/package/koa-middlewares](https://www.npmjs.com/package/koa-middlewares))
```javascript
var koa = require('koa');
var middlewares = require('koa-middlewares');
var router = middlewares.router();
var app = koa();
router.get('/', function *(){
this.body = 'hello koa-middlewares';
});
app.use(middlewares.bodyParser());
app.use(middlewares.conditional());
app.use(middlewares.etag());
app.use(middlewares.compress());
middlewares.csrf(app);
app.use(router.routes());
app.use(router.allowedMethods());
app.listen(7001);
看起来非常恐怖,这也是为什么会有很多基于 koa 的 web 开发框架,针对特定的功能整合了相应的中间件,并为开发者处理了 web 安全、性能等相关问题,接下来介绍其中的佼佼者——企业级的 web 开发框架 egg.js