koa2的源码比较简单,重点解读aplication,
其中context源码比较简单,主要是一些error cookies等,重点可以关注下delegate,delegate模块中,主要通过prototype的方式进行属性的增加。
request和response两个模块都是get set的一些基础api及封装的node原始方法
application
'use strict'; //严格模式 /** * Module dependencies. */ const isGeneratorFunction = require('is-generator-function'); const debug = require('debug')('koa:application'); const onFinished = require('on-finished'); const response = require('./response'); const compose = require('koa-compose'); const isJSON = require('koa-is-json'); const context = require('./context'); const request = require('./request'); const statuses = require('statuses'); const Emitter = require('events'); const util = require('util'); const Stream = require('stream'); const http = require('http'); const only = require('only'); const convert = require('koa-convert'); const deprecate = require('depd')('koa'); const { HttpError } = require('http-errors'); /** * constructor() 构造函数 * listen() 调用原生http模块创建服务并监听 * use() 中间件处理 * callback() http请求的回调函数 * handleRequest() 请求真正的回调函数 * createContext() 创建上下文对象 * respond() 所有中间件处理完后自动响应 * onerror() 处理错误信息 * */ /** * Expose `Application` class. * Inherits from `Emitter.prototype`. */ module.exports = class Application extends Emitter { /** * Initialize a new `Application`. * * @api public */ /** * * @param {object} [options] Application options * @param {string} [options.env='development'] Environment * @param {string[]} [options.keys] Signed cookie keys * @param {boolean} [options.proxy] Trust proxy headers * @param {number} [options.subdomainOffset] Subdomain offset * */ constructor(options) { super(); options = options || {}; this.proxy = options.proxy || false; //是否允许跨域 this.subdomainOffset = options.subdomainOffset || 2; // 子域名允许请求几级连接 this.env = options.env || process.env.NODE_ENV || 'development'; //node的执行环境 if (options.keys) this.keys = options.keys; this.middleware = []; //所有的中间件的存入 this.context = Object.create(context); //每次实例化都重新赋值,为保证多次实例化时保持不冲突,和单例模式成反例 this.request = Object.create(request); this.response = Object.create(response); if (util.inspect.custom) { this[util.inspect.custom] = this.inspect; //保存88行代码中的内容 } } /** * Shorthand for: * * http.createServer(app.callback()).listen(...) * * @param {Mixed} ... * @return {Server} * @api public */ listen(...args) { debug('listen'); const server = http.createServer(this.callback()); //原生http模块创建服务并监听 return server.listen(...args); } /** * Return JSON representation. * We only bother showing settings. * * @return {Object} * @api public */ toJSON() { return only(this, [ //only 对传入的数据使用reduce进行重组 'subdomainOffset', 'proxy', 'env' ]); } /** * Inspect implementation. * * @return {Object} * @api public */ inspect() { return this.toJSON(); //数据重组 } /** * Use the given middleware `fn`. * * Old-style middleware will be converted. * * @param {Function} fn * @return {Application} self * @api public */ use(fn) { if (typeof fn !== 'function') throw new TypeError('middleware must be a function!');//必须是一个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 || '-'); //DEBUG=koa* node --harmony app.js 调试时输出中间件调用及时长 this.middleware.push(fn); //将中间件加入到middleware数组中 return this; } /** * Return a request handler callback * for node's native http server. * * @return {Function} * @api public */ callback() { const fn = compose(this.middleware); //将这些中间件组合后拿到执行链函数fn if (!this.listenerCount('error')) this.on('error', this.onerror); //如果没有监听则报错 const handleRequest = (req, res) => { //事件处理函数 const ctx = this.createContext(req, res); //创建一个ctx return this.handleRequest(ctx, fn); //交给157行的handleRequest }; return handleRequest; } /** * Handle request in callback. * * @api private */ handleRequest(ctx, fnMiddleware) { const res = ctx.res; res.statusCode = 404; //初始赋值 const onerror = err => ctx.onerror(err); const handleResponse = () => respond(ctx); //211行详解 onFinished(res, onerror); return fnMiddleware(ctx).then(handleResponse).catch(onerror); } /** * Initialize a new context. * * @api private */ createContext(req, res) { const context = Object.create(this.context);//通过context对象的原型创建 const request = context.request = Object.create(this.request);//通过request对象的原型创建,this.request指的是原生的request,修改this.request中的属性就是修改原生的对应的属性数据 const response = context.response = Object.create(this.response);//通过response对象的原型创建 context.app = request.app = response.app = this; //传递 context.req = request.req = response.req = req; context.res = request.res = response.res = res; request.ctx = response.ctx = context; request.response = response; //交叉传递 response.request = request; context.originalUrl = request.originalUrl = req.url; context.state = {}; return context; } /** * Default error handler. * * @param {Error} err * @api private */ onerror(err) { if (!(err instanceof Error)) throw new TypeError(util.format('non-error thrown: %j', err)); //检测err不是Error实例时,创建一个Error的实例 if (404 == err.status || err.expose) return; if (this.silent) return; const msg = err.stack || err.toString(); //将err堆栈的信息拿出来 console.error(); //控制台打印Error信息 console.error(msg.replace(/^/gm, ' ')); console.error(); } }; /** * Response helper. */ function respond(ctx) { // allow bypassing koa if (false === ctx.respond) return; //允许绕过KOA 为写入原始的res对象而不是让koa处理你的rsponse if (!ctx.writable) return; const res = ctx.res; let body = ctx.body; //外面Middleware传入的body数据 const code = ctx.status; //当前状态码 // ignore body if (statuses.empty[code]) { //当前状态码是空的,则清掉body并结束 // strip headers ctx.body = null; return res.end(); } if ('HEAD' == ctx.method) { //head部分 if (!res.headersSent && isJSON(body)) { //判断当前header没有被发送并且是 重组后的json数据 ctx.length = Buffer.byteLength(JSON.stringify(body)); //则重新序列化 取长度 } return res.end(); } // status body if (null == body) { // body部分 不为null if (ctx.req.httpVersionMajor >= 2) { //根据http major的版本 分别对body进行初始化 body = String(code); } else { body = ctx.message || String(code); } if (!res.headersSent) { ctx.type = 'text'; ctx.length = Buffer.byteLength(body); } return res.end(body); } //以下为支持各种格式的body // responses if (Buffer.isBuffer(body)) return res.end(body); if ('string' == typeof body) return res.end(body); if (body instanceof Stream) return body.pipe(res); // body: json body = JSON.stringify(body); if (!res.headersSent) { ctx.length = Buffer.byteLength(body); } res.end(body); } /** * Make HttpError available to consumers of the library so that consumers don't * have a direct dependency upon `http-errors` */ module.exports.HttpError = HttpError;