先看看這個極簡的啓動代碼:
node
const Koa = require('koa');
const app = new Koa();
// response
app.use(ctx => {
ctx.body = 'Hello Koa';
});
app.listen(3000);複製代碼
咱們在koa源碼文件夾下建立index.js文件, 將上面的代碼寫入,並將require('koa')
換成 require('.')
git
const Koa = require('.')
debugger
const app = new Koa();
app.use(ctx => {
ctx.body = 'Hello Koa';
});
app.listen(3000);
複製代碼
而後進入目錄運行node --inspect-brk index.js
github
在chrome瀏覽器打開調試 chrome://inspectchrome
1.引入Koajson
require 會依照這個順序去查找須要的文件
數組
Module._extensions={
.js:funciton(module,filename),
.json:funciton(module,filename),
.node:funciton(module,filename)
}複製代碼
經過讀取package.json中的main字段獲得完整路徑promise
查找到路徑以後經過 fs.readFileSync加載模塊瀏覽器
讀取文件後開始編譯,首先將讀取的代碼script,進行組裝,bash
即頭部添加(function (exports, require, module, __filename, __dirname) { ', cookie
'\n});'
NativeModule.wrap = function(script) {
return NativeModule.wrapper[0] + script + NativeModule.wrapper[1];
};
NativeModule.wrapper = [
'(function (exports, require, module, __filename, __dirname) { ',
'\n});'
];複製代碼
這就是爲何每一個模塊沒有定義exports, require, module, __filename, __dirname變量,卻能使用的緣由
new Koa()
的時候就是new的這個對象, 最關鍵的是建立了context,request,response對象
constructor() {
super();
this.proxy = false;
// 中間件初始化爲一個列表
this.middleware = [];
this.subdomainOffset = 2;
// 默認爲開發環境
this.env = process.env.NODE_ENV || 'development';
// 建立Context, Request,Response對象,爲何要用Object.create函數呢?
this.context = Object.create(context);
this.request = Object.create(request);
this.response = Object.create(response);
}複製代碼
use(fn) {
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);
}
this.middleware.push(fn);
return this;
}複製代碼
listen(...args) {
debug('listen');
const server = http.createServer(this.callback());
return server.listen(...args);
}
複製代碼
callback() {
// compose函數將註冊的中間件組合成了一個函數,相似於遞歸
const fn = compose(this.middleware);
// Koa Application 擴展了 Emitter類, listeners 是Emitter類的屬性.
// 這裏表示若沒有註冊error事件處理函數, 則註冊一個
if (!this.listeners('error').length) this.on('error', this.onerror);
// 返回的請求處理函數, req, res是createServer回調時會傳入的nodejs請求和響應對象。
const handleRequest = (req, res) => {
// 默認的的狀態碼爲404
res.statusCode = 404;
// 建立koa應用的上下文, context將不少屬性和方法都代理到這個對象上方便開發.
const ctx = this.createContext(req, res);
// 使用 ctx.onerror處理請求錯誤, 詳見Context
const onerror = err => ctx.onerror(err);
// 處理響應函數
const handleResponse = () => respond(ctx);
// 請求完成以後若是出錯調用onerror
onFinished(res, onerror);
// 等中間件都處理完了以後處理響應
return fn(ctx).then(handleResponse).catch(onerror);
};
return handleRequest;
}
複製代碼
// req, res 是node的原生請求響應對象,是全部信息的來源.
// request,response是koa提供的方便咱們開發使用的請求響應對象.
createContext(req, res) {
const context = Object.create(this.context);
const request = context.request = Object.create(this.request);
const response = context.response = Object.create(this.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;
// originalUrl即 req.url
context.originalUrl = request.originalUrl = req.url;
// cookies 直接使用的第三方庫
context.cookies = new Cookies(req, res, {
keys: this.keys,
secure: request.secure
});
// 更經常使用的從ip中讀取請求方的IP地址, ips是?
request.ip = request.ips[0] || req.socket.remoteAddress || '';
// 使用accepts設置請求能接受的內容類型
context.accept = request.accept = accepts(req);
// ?
context.state = {};
return context;
}
複製代碼
this.body = 'xxx'
而後函數返回,不須要咱們手動調用res.end(),由於koa已經幫咱們封裝好了.
/**
* Response helper.
*/
function respond(ctx) {
// ...
// 若是HTTP狀態碼錶示內容應該爲空,則設空返回
if (statuses.empty[code]) {
ctx.body = null;
return res.end();
}
if ('HEAD' === ctx.method) {
// 要求返回響應頭,若是headersSent爲false
if (!res.headersSent && isJSON(body)) {
ctx.length = Buffer.byteLength(JSON.stringify(body));
}
return res.end();
}
// 若是沒有設置body,只設置了status,則用狀態碼或message設置body.
if (null == body) {
body = ctx.message || String(code);
if (!res.headersSent) {
ctx.type = 'text';
ctx.length = Buffer.byteLength(body);
}
return res.end(body);
}
// koa 支持Buffer, string, Stream類型的數據
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 處理普通json類型返回.
body = JSON.stringify(body);
if (!res.headersSent) {
ctx.length = Buffer.byteLength(body);
}
res.end(body);
}
複製代碼