koa2的源碼比較簡單,重點解讀aplication,node
其中context源碼比較簡單,主要是一些error cookies等,重點能夠關注下delegate,delegate模塊中,主要經過prototype的方式進行屬性的增長。git
request和response兩個模塊都是get set的一些基礎api及封裝的node原始方法github
applicationjson
'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;