最近讀了 koa2 的原始碼,理清楚了架構設計與用到的第三方庫。本系列將分為 3 篇,分別介紹 koa 的架構設計和 3 個核心庫,最終會手動實現乙個簡易的 koa。這是系列第 2 篇,關於 3 個核心庫的原理。
所有系列文章都放在了github。歡迎交流和star✿✿ ヽ(°▽°)ノ ✿
koa2 種推薦使用 async 函式,koa1 推薦的是 generator。koa2 為了相容,在呼叫use
新增中介軟體的時候,會判斷是否是 generator。如果是,則用covert
庫轉化為 async 函式。
判斷是不是 generator 的邏輯寫在了 is-generator-function 庫中,邏輯非常簡單,通過判斷object.prototype.tostring.call
的返回結果即可:
function
*say()
object.prototype.tostring.
call
(say)
;// 輸出: [object generatorfunction]
delegates和 koa 一樣,這個庫都是出自大佬 tj 之手。它的作用就是屬性**。這個**庫常用的方法有getter
,setter
,method
和access
。
假設準備了乙個物件target
,為了方便訪問其上request
屬性的內容,對request
進行**:
const delegates =
require
("delegates");
const target =}}
;delegates
(target,
"request").
getter
("name").
setter
("name").
method
("say"
);
**後,訪問request
將會更加方便:
console.
log(target.name)
;// xintan
target.name =
"xintan!!!"
;console.
log(target.name)
;// xintan!!!
target.
say();
// hello
對於setter
和getter
方法,是通過呼叫物件上的__definesetter__
和__definegetter__
來實現的。下面是單獨拿出來的邏輯:
/**
* @param proto 被**物件
* @param property 被**物件上的被**屬性
* @param name
*/function
mydelegates
(proto, property, name));
proto.
__definesetter__
(name,
function
(val));
}mydelegates
(target,
"request"
,"name");
console.
log(target.name)
;// xintan
target.name =
"xintan!!!"
;console.
log(target.name)
;// xintan!!!
剛開始我的想法是更簡單一些,就是直接讓proto[name] = proto[property][name]
。但這樣做有個缺點無法彌補,就是之後如果proto[property][name]
改變,proto[name]
獲取不了最新的值。
對於method
方法,實現上是在物件上建立了新屬性,屬性值是乙個函式。這個函式呼叫的就是**目標的函式。下面是單獨拿出來的邏輯:
/**
* * @param proto 被**物件
* @param property 被**物件上的被**屬性
* @param method 函式名
*/function
mydelegates
(proto, property, method);}
mydelegates
(target,
"request"
,"say");
target.
say();
// hello
koa 中也有對屬性的access
方法**,這個方法就是getter
和setter
寫在一起的語法糖。
koa 最讓人驚豔的就是大名鼎鼎的「洋蔥模型」。以至於之前我在開發 koa 中介軟體的時候,一直有種 magic 的方法。經常疑惑,這裡await next()
,執行完之後的中介軟體又會重新回來繼續執行未執行的邏輯。
這一段邏輯封裝在了核心庫koa-compose 裡面。原始碼也很簡單,算上各種注釋只有不到 50 行。為了方便說明和理解,我把其中一些意外情況檢查的**去掉:
function
compose
(middleware)
catch
(err)}
};}
middleware 裡面儲存的就是開發者自定義的中介軟體處理邏輯。為了方便說明,我準備了 2 個中介軟體函式:
const middleware =
[async
(ctx, next)
=>
,async
(ctx, next)
=>
];
現在,模擬在 koa 中對 compose 函式的呼叫,我們希望程式的輸出是:a b c
(正如使用 koa 那樣)。執行以下**即可:
const fns =
compose
(middleware)
;fns()
;
ok,目前已經模擬出來了乙個不考慮異常情況的洋蔥模型了。
為什麼會有洋蔥穿透的的效果呢?回到上述的compose
函式,閉包寫法返回了乙個新的函式,其實就是返回內部定義的dispatch
函式。其中,引數的含義分別是:
在上面的測試用例中,fns
其實就是dispatch(0)
。在dispatch
函式中,通過引數 i 拿到了當前要執行的中介軟體fn
。
return promise.
resolve(fn
(context, dispatch.
bind
(null
, i +1)
));
那麼,在中介軟體中執行await next()
,其實就是執行:await dispatch.bind(null, i + 1)
。因此看起來,當前中介軟體會停止自己的邏輯,先處理下乙個中介軟體的邏輯。
因為每個dispatch
,都返回新的 promsise。所以async
會等到 promise 狀態改變後再回來繼續執行自己的邏輯。
最後,在不考慮 koa 的上下文環境的情況下,用 async/await 的提煉出了 compose 函式:
function
compose
(middleware)
catch
(err)}
}
下面是它的使用方法:
const middleware =
[async next =>
,async next =>];
compose
(middleware)
;// 輸出a b c
希望最後這段**能幫助理解! Koa原始碼分析(二) co的實現
koa原始碼分析 一 generator koa原始碼分析 二 co的實現 koa原始碼分析 三 middleware機制的實現 大名鼎鼎的co是什麼?它是tj大神基於es6的一些新特性開發的非同步流程控制庫,基於它所開發的koa被視為未來主流的web框架。koa基於co實現,而co又是使用了es6...
springmvc核心原理及原始碼分析
1 dispatherservlet顧名思義乙個排程的servlet,是乙個front controller 前端控制器 也可以說是springmvc的c位,負責接受客戶端的request,並將這些request分配給對應的處理元件 3.dispatcherservlet 根據獲得的handler,...
深入剖析PHP7核心原始碼(二) PHP變數容器
php的變數使用起來非常方便,其基本結構是底層實現的zval,php7採用了全新的zval,由此帶來了非常大的效能提公升,本文重點分析php7的zval的改變。typedef struct zval struct zval typedef union zvalue value str hashtab...