Koa2源码解析
Koa 是一个新的 web 框架,由 Express 幕后的原班人马打造, 致力于成为 web 应用和 API 开发领域中的一个更小、更富有表现力、更健壮的基石。被称为基于Node.js的下一代开发平台框架。在这里主要分析了koa2的主要的运行流程以及相关源代码,
从一个简单的实例开始:
const Koa = require('koa')
const app = new Koa()
app.use(async (ctx, next) => {
ctx.body = { hello: 'world'}
})
app.listen(3000, function() {
console.log("server running at 3000")
})
在这个简单的实例中主要做了三件事
1. 新建Koa实例
2. 调用use函数
3. 调用listen
由此可知, Koa的入口主要是use和listen函数,所以我们就从这两个函数入手看看Koa到底做了哪些事情
use函数(koa/lib/application.js):
主要功能就是把传进来的函数放入一个数组中
use(fn) {
if (typeof fn !== 'function') throw new TypeError('middleware must be a function!');
if (isGeneratorFunction(fn)) {
deprecate('Support for generators will be removed in v3. ' +
'See the documentation for examples of how to convert old middleware ' +
'https://github.com/koajs/koa/blob/master/docs/migration.md');
fn = convert(fn);
}
debug('use %s', fn._name || fn.name || '-');
this.middleware.push(fn);
return this;
}
listen函数(koa/lib/application.js):
listen函数任务就是新建一个的httpServer, 将参数传入http.createServer的参数中, 在这里调用了this.callback函数,由此可知this.callback()函数应该是返回了一个createServer的回调函数,接下来让我们看看koa中的this.callback函数做了什么
listen(...args) {
debug('listen');
const server = http.createServer(this.callback());
return server.listen(...args);
}
callback函数(koa/lib/application.js):
callback函数首先将use中传入函数组成的数组的进行整合中间件, 然后创建一个handleRequest函数并返回, 所以这个handlerRequest函数就是就是httpServer中用来处理请求和相应的函数
handlerRequest做了两件事:
- 创建一个ctx对象, 将request和response挂载到ctx对象上面
- 将请求交给this.handleRequest处理
callback() {
const fn = compose(this.middleware);
if (!this.listenerCount('error')) this.on('error', this.onerror);
const handleRequest = (req, res) => {
// 将req和res对象挂载context对象上
const ctx = this.createContext(req, res);
return this.handleRequest(ctx, fn);
};
return handleRequest;
}
- handleRequest函数(koa/lib/application.js): handleRequest函数做的任务非常简单,将ctx对象交给中间件(fnMiddleware)去处理, 然后将处理结果交给response(handleResponse)去做响应
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);
}
compose函数(koa-compose/index.js)
接下来就来看一下koa中最重要要的compose函数(koa-compose/index.js)做了什么
compose函数返回的是一个函数,就是在handleRequest中使用的fnMidlleware函数
在fnMiddleware函数中调用了dispatch
函数,dispatch
函数的作用就是执行我们在use函数的那个传入的函数, 最终返回一个Promise对象,将返回结果交给handleResponse相应
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!')
}
return function (context, next) {
let index = -1
// 调用dispatch函数
return dispatch(0)
// 定义dispatch函数
function dispatch (i) {
if (i <= index) return Promise.reject(new Error('next() called multiple times'))
index = i
let fn = middleware[i]
if (i === middleware.length) fn = next
if (!fn) return Promise.resolve()
try {
return Promise.resolve(fn(context, dispatch.bind(null, i + 1)));
} catch (err) {
return Promise.reject(err)
}
}
}
}
到这里Koa核心的运行流程就基本结束,执行过程相对比较简单,分析过程中省略了一些步骤,比如Koa是如何封装request和response对象的,如何将request和response对象挂载在ctx对象上面的,又是如何将数据响应回去的,感兴趣的兄弟可以自己去koa源码里面深入研究一下