context.js
-
context.js
裡面放的就是用於新建上下文物件的原型程式碼。除了一個處理錯誤的onerror
方法以外,主要用了一個delegates
模組,進行屬性和方法的繫結。
- 比如:
delegate(proto, 'response')
.method('attachment')
.method('redirect')
.method('remove')
.method('vary')
.method('set')
.method('append')
.method('flushHeaders')
.access('status')
.access('message')
.access('body')
.access('length')
.access('type')
.access('lastModified')
.access('etag')
.getter('headerSent')
.getter('writable');
- 上述的程式碼就是將
ctx.response
上面的方法和屬性,代理繫結到ctx
上。比如:繫結完成後呼叫ctx.redirect
,其實就是在呼叫ctx.response.redirect
。
-
access
與getter
都是用來繫結屬性,不過getter
繫結的資料是隻讀的,access
繫結的屬性才是可讀可寫的。
Delegator
- 下面來看看Delegator裡用於繫結屬性和方法的原始碼
-
method
用於代理方法,proto
是ctx
的原型,target
對應的是ctx
上面的response
或request
。
Delegator.prototype.method = function(name){
var proto = this.proto;
var target = this.target;
this.methods.push(name);
proto[name] = function(){
return this[target][name].apply(this[target], arguments);
};
return this;
};
-
access
用於繫結可重寫的屬性、getter
定義只讀屬性
Delegator.prototype.access = function(name){
return this.getter(name).setter(name);
};
Delegator.prototype.setter = function(name){
var proto = this.proto;
var target = this.target;
this.setters.push(name);
proto.__defineSetter__(name, function(val){
return this[target][name] = val;
});
return this;
};
Delegator.prototype.getter = function(name){
var proto = this.proto;
var target = this.target;
this.getters.push(name);
proto.__defineGetter__(name, function(){
return this[target][name];
});
return this;
};
- 稍微總結一下,每個方法最後都會返回
this
,這樣可以支援鏈式呼叫。
-
__defineGetter__
和__defineSetter__
並不是標準的API,不存在於ECMAScript中,並不推薦使用。可以用Object. defineProperty
實現同樣的功能。
- 實現代理時,是將所有的屬性和方法繫結到了
ctx
的原型上面去了。
koa-compose
- 這個模組就只封裝了一個
compose
方法,用於組合排版koa的中介軟體。首先對middleware
和他的元素進行型別判斷。然後返回一個遞迴執行的函式。
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) {
// last called middleware #
let index = -1
return dispatch(0)
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, function next () {
return dispatch(i + 1)
}))
} catch (err) {
return Promise.reject(err)
}
}
}
}
- 從上面程式碼再結合上一篇程式碼中的
handleRequest
和fnMiddleware
方法,可以瞭解到koa中介軟體的機制(後面單獨再寫一篇吧)。