前言
最近在一次面試中被問到 koa 裡面的 delegates、request、respone、req、res之間的關係?我當時只回答了 koa-compose遞迴原理,雖然最後通過了面試,但是我覺得還是有必要追其原因,因為我沒回答出來。
Koa 原始碼 createContext(req,res)
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;
context.originalUrl = request.originalUrl = req.url;
context.state = {};
return context;
}
複製程式碼
前提
首先我們必須先了解一下程式碼
this.context = Object.create(context);
this.request = Object.create(request);
this.response = Object.create(response);
複製程式碼
分析 context
-
context
const context = require('./context'); this.context = Object.create(context); 複製程式碼
1.1. context 幹了什麼?
const delegate = require('delegates'); const proto = module.exports = { ... } delegate(proto, 'response') .method('attachment') .method('redirect').. delegate(proto, 'request') .method('acceptsLanguages') .method('acceptsEncodings')... 複製程式碼
1.2. delegates幹了什麼?
module.exports = Delegator; function Delegator(proto, target) {} Delegator.auto = function(proto, targetProto, targetProp){} Delegator.prototype.method = function(name){ proto[name] = function(){ return this[target][name].apply(this[target], arguments); }; return this; } Delegator.prototype.access = function(name){ return this.getter(name).setter(name); }; Delegator.prototype.getter = function(name){ proto.__defineGetter__(name, function(){ return this[target][name]; }); return this; } Delegator.prototype.setter = function(name){ proto.__defineSetter__(name, function(val){ return this[target][name] = val; }); return this; }; 複製程式碼
常用的 method 方法內部實現:
proto[name] = function(){ return this[target][name].apply(this[target], arguments); }; 複製程式碼
其實就是給context.js 返回的物件proto 對於request 、response 屬性增加增、讀取、設定、改變一些方法,每個方法:
分析 request.js 和 response.js
```
module.exports ={
...
}
request.js 封裝了req ,response.js封裝了res
```
複製程式碼
分析-1:
const context = Object.create(this.context);
const request = context.request = Object.create(this.request);
const response = context.response = Object.create(this.response);
複製程式碼
先建立context物件、然後為context上下文增加request、request屬性。現在context除了context.js檔案匯出的proto物件的方法和屬性外、以及利用delegates在proto.request、proto.response上面定義的方法外,現在又在context.request、context.respons上面增加了分別對應request.js 和 response.js 預設匯出的方法,request.js 又封裝了req ,response.js封裝了res。
分析-2:
context.app = request.app = response.app = this;
context.req = request.req = response.req = req;
context.res = request.res = response.res = res;
複製程式碼
- 整個context返回的內容:
- 我們發現context物件裡面有app這個屬性,並且request、response都有app、req、res
分析-3
request.ctx = response.ctx = context;
request.response = response;
response.request = request;
複製程式碼
從上面的圖我們可以看出 context.request.ctx response 、context.response.ctx request 都有對應的屬性
分析-4
context.originalUrl = request.originalUrl = req.url;
context.state = {};
複製程式碼
在context上下文中我們可以檢視originalUrl、state
總結:
const Koa = require("koa");
const app = new Koa();
app.use(async ctx => {
ctx.body = 'Hello World';
});
app.listen(3000);
複製程式碼
在koa 原始碼createContext函式裡面console.log(context) 就可以看出整個函式返回context是什麼了。