简介
Koajs
www.koajs.com.cn
优势
Koa 是一个轻量级的、极富表现力的 http 框架。
一个web request会通过 Koa 的中间件栈,来动态完成 response 的处理。
Koa2 采用了 async 和 await 的语法来增强中间件的表现力。
Koa 不在内核方法中绑定任何中间件,它仅仅提供了一个轻量优雅的函数库。
带着问题
1、koa洋葱模型怎么实现的?
2、怎么启动(初始化),做了那些事情?
3、一个请求进来如何处理的流程?
koa github
github.com/koajs/koa/t…
koa初始化
1、new Koa 初始化一个实例
// app.js
const Koa = require('koa');
// 1初始化一个Koa
const app = new Koa();
复制代码
2、app.use搜集中间件到middleware数组
// app.js
// 搜集中间件到middleware数组
// 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 = {};
});
复制代码
3、app.listen 合成中间件fnMiddleware,返回一个callback函数给http.createServer,开启服务器,等待http请求。
- koa application里的响应回调。
中间件用来处理请求流程
koa-compose github.com/koajs/compo…
// middleware [fn1, fn2, fn3]
function compose (middleware) {
if (!Array.isArray(middleware)) throw new TypeError('Middleware stack must be an array!')
for (const fn of middleware) {
if (typeof fn !== 'function') throw new TypeError('Middleware must be composed of functions!')
}
/**
* @param {Object} context
* @return {Promise}
* @api public
*/
return function (context, next) {
// last called middleware #
let index = -1
return dispatch(0)
function dispatch (i) {
// 防止多次调用
if (i <= index) return Promise.reject(new Error('next() called multiple times'))
index = i
// fn就是当前的中间件
let fn = middleware[i]
if (i === middleware.length) fn = next // 最后一个中间件如果也next时进入(一般最后一个中间件是直接操作ctx.body,并不需要next了)
if (!fn) return Promise.resolve() // 没有中间件,直接返回成功
try {
/*
* 使用了bind函数返回新的函数,类似下面的代码
return Promise.resolve(fn(context, function next () {
return dispatch(i + 1)
}))
*/
// dispatch.bind(null, i + 1)就是中间件里的next参数,调用它就可以进入下一个中间件
// fn如果返回的是Promise对象,Promise.resolve直接把这个对象返回
// fn如果返回的是普通对象,Promise.resovle把它Promise化
return Promise.resolve(fn(context, dispatch.bind(null, i + 1)));
} catch (err) {
return Promise.reject(err)
}
}
}
}
复制代码
// 文件位置 ./node_modules/koa/lib/application.js
callback() {
const fn = compose(this.middleware);
if (!this.listenerCount('error')) this.on('error', this.onerror);
const handleRequest = (req, res) => {
const ctx = this.createContext(req, res);
return this.handleRequest(ctx, fn);
};
return handleRequest;
}
listen(...args) {
debug('listen');
const server = http.createServer(this.callback());
return server.listen(...args);
}
复制代码
// app.js
// listen
app.listen(3000);
console.log('app', Koa);
复制代码
处理请求流程
每次请求,createContext生成一个新的ctx,传给fnMiddleware,触发中间件的整个流程
handleRequest(ctx, fnMiddleware) {
const res = ctx.res;
res.statusCode = 404;
const onerror = err => ctx.onerror(err);
const handleResponse = () => respond(ctx);
onFinished(res, onerror);
return fnMiddleware(ctx).then(handleResponse).catch(onerror);
}
复制代码
总结
Koa是一个设计非常精简的Web框架(库),源代码本身不含任何中间件,可以使我们根据自身需要去组合一些中间件使用,也可以自身去开发一些中间件。它结合async/await实现了洋葱模式。
问题
koa的洋葱圈模型怎样实现的?
// 这样就可能更好理解了。
// simpleKoaComposeconst
[fn1, fn2, fn3] = this.middleware;
const fnMiddleware = function(context) {
return Promise.resolve(fn1(context,
function next() {
return Promise.resolve(fn2(context,
function next() {
return Promise.resolve(fn3(context,
function next() {
return Promise.resolve();
}))
}))
}));
};
fnMiddleware(ctx).then(handleResponse).catch(onerror);
复制代码
app.use() 把中间件函数存储在middleware数组中,
最终会调用koa-compose导出的函数compose返回一个promise,中间函数的第一个参数ctx是包含响应和请求的一个对象,会不断传递给下一个中间件。next是一个函数,返回的是一个promise。 最后一个中间件调next 直接返回Promise.resolve();
a, b, c 三个中间件
return a(ctx,b(ctx,c(ctx, next))