在用 PHP 開發本身博客後臺的時候,碰到一個問題:通常的後臺框架都會提供中間件,給使用者自定義額外的功能,so,PHP 應該如何整合一系列的功能中間件呢?html
註冊中間件的時候將其存到數組中,執行時遍歷數組。express
const app = new App();
// 註冊中間件
// this.middlewares.push(middleware);
app.use(middleware);
// 使用中間件
// 內部方法實現
function __internal() {
// some injections here.
for (let i = 0; i < this.middlewares.length; i++) {
const fn = this.middlewares[i];
// 你能夠往中間件注入一些東西
fn(injection);
}
}
複製代碼
最簡單、最笨的一種方式。來看看其餘大佬怎麼作的。編程
應用了函數式編程盛行的「函數合成(compose
)」。簡而言之就是,將多箇中間件函數合成一個函數,最後只執行一次。redux
redux
中間件的註冊方式是一次性的,無需屢次調用 .use
或其它用於註冊的函數:數組
applyMiddleware(middleware1, middlwware2, ...);
複製代碼
來看看 applyMiddleware
實現:app
// applyMiddleware.js
// middlewareAPI 是咱們注入中間件的一些東西
const chain = middlewares.map(middleware => middleware(middlewareAPI))
// compose 全部中間件,以後一次性調用 dispatch
dispatch = compose(...chain)(store.dispatch)
複製代碼
compose
在這裏不在贅述。想了解的老鐵們能夠去瞅下 阮一峯的函數式編程入門。框架
將註冊的中間件存到 middleware
數組中,執行時調用 compose
以後的函數。koa
// application.js
class Application {
use(fn) {
// 註冊時
this.middleware.push(fn);
}
callback() {
// compose 成一個 fn 函數調用
const fn = compose(this.middleware);
}
}
複製代碼
koa-compose
與函數式編程概念中的 compose
略有不一樣,採用「遞歸 + Promise」的方式,造成了其獨特的「洋蔥模型」。async
// koa-compose/index.js
return dispatch(0);
function dispatch(i) {
let fn = middleware[i];
// 終止條件
if (!fn) return Promise.resolve();
// 遞歸調用
// dispatch 就是往中間件注入的 next
// 從這裏咱們也能夠看到爲何寫中間件時必定要調用 next 方法,
// 若是不調用的話,遞歸不會繼續,以後的而中間件也不執行,一直 pending
return Promise.resolve(fn(context, dispatch.bind(null, i + 1)));
}
複製代碼
這樣也很難理解爲何 await next()
以後的代碼會反序執行,來看個簡單的例子:函數式編程
async function foofoo() {
console.log(2);
await Promise.resolve();
console.log(2);
}
async function foo() {
console.log(1);
await foofoo();
console.log(1);
}
a(); // 1 2 2 1
複製代碼
仍是隻可意會,不可言傳[奸笑]。關鍵點在於遞歸。
註冊成數組(額外的實例化成 layer
)形式,調用時從第一個開始執行,執行完畢後調用 next
找到第二個,第二個執行完後 next
第三個,直至最後。
express
中間件的應用實際上是到 router
上面,全部咱們直接跳到路由的設計:
// router/index.js
// 註冊時
proto.use = function use(fn) {
// 實例化 layer
var layer = new Layer(options, fn);
this.stack.push(layer);
};
// 執行時
proto.handle = function handle(req, res, next) {
// 生成引用。要注意 this 這個坑
var stack = this.stack;
next();
// 這個 next 就是咱們注入了中間件裏
function next(err) {
while (match !== true && idx < stack.length) {
layer = stack[idx ++];
match = matchLayer(layer, path);
// 若是你調用 next(1) 的話,這裏會一直不匹配!
// 最後直接跳事後面的中間件返回 1
if (layerError) {
match = false;
continue;
}
}
// 找到後
// 其實內部也就是執行咱們的 middleware 函數
return layer.handle_request(req, res, next);
}
};
複製代碼
跟 for 循環很像,可是又依賴於開發者手動 next
觸發下一個。
講了半天中間件,和我要的 compose
有半毛錢關係哦。
redux
用到了精髓koa
有點皮毛東西express
八竿子打不着強行 pick 一波:若是你還在古老地循環調用一系列函數,不妨 compose
試下。