什么是 中間件
在 JavaScript 中,中間件是指在應(yīng)用程序的請求-響應(yīng)周期中,位于請求和最終處理程序之間的函數(shù)。換句話說,中間件其實就是一個函數(shù),一個執(zhí)行特定邏輯的函數(shù)。

image2024-6-21_17-53-32.png
中間件的其中之一的作用
擴(kuò)展(增強)函數(shù)的功能(從開始 ... 結(jié)束 中間要加入的部分)(eg:調(diào)用alert函數(shù)方法打印日志)
let next = window.alert;
window.alert = function alertAndLog(action) {
console.log('alert', action);
next(action);
console.log('after');
};
alert(1)
// redux 在dispatch 時
const next = store.dispatch
store.dispatch = function dispatchAndLog(action) { console.log('dispatching', action)
let result = next(action)
console.log('next state', store.getState())
return result
}
中間件使用場景
-
Redux 中間件:在 Redux 中,中間件可以用來處理 action 和 reducer 之間的一些操作,例如日志記錄、異步操作等。常見的 Redux 中間件包括 redux-thunk、redux-saga 等。
-
Vue.js 中間件:Vue.js提供了路由守衛(wèi) (router guards) 的機制,可以在路由導(dǎo)航過程中使用中間件來進(jìn)行身份驗證、日志記錄等操作。
-
日志記錄:中間件可以用于記錄請求的信息,例如請求方法、URL、響應(yīng)狀態(tài)碼等,以便后續(xù)分析和監(jiān)控。
-
身份驗證和授權(quán):通過中間件,可以對請求進(jìn)行身份驗證和授權(quán)檢查,以確保用戶有權(quán)訪問特定資源。
-
等等
如何實現(xiàn)一個中間件
function Pipeline(...middlewares) {
const stack = middlewares
const push = (...middlewares) => {
stack.push(...middlewares)
}
const execute = async (context) => {
let prevIndex = -1
const runner = async (index) => {
if (index === prevIndex) {
throw new Error('next() called multiple times')
}
prevIndex = index
const middleware = stack[index]
if (middleware) {
// 執(zhí)行當(dāng)前中間件
// 傳入的第二個參數(shù)即 `next()`,表示執(zhí)行下一個中間件
await middleware(context, () => {
return runner(index + 1)
})
}
}
await runner(0)
}
}
// 創(chuàng)建一個中間件 pipeline
const pipeline = Pipeline(
// 傳入一個初始中間件
(ctx, next) => {
console.log(ctx)
next()
}
)
// 添加一些中間件
pipeline.push(
(ctx, next) => {
ctx.value = ctx.value + 21
next()
}
)
// 添加終止中間件
pipeline.push((ctx, next) => {
console.log(ctx)
// 不調(diào)用 `next()`
})
pipeline.push((ctx, next) => {
console.log('當(dāng)然,這個中間件不會被調(diào)用,這塊消息也不會打印')
})
// 使用初始值 `{ value: 0 }`(即 `ctx` 的值)執(zhí)行 pipeline
pipeline.execute({ value: 0 })
koa 中洋蔥模型的實現(xiàn)

koa-middlewares-onion (1).jpg
// 輸出
// 1
// 1614932795751
// 2
// 3
// 3after
// 2after
// 1after
// GET / - 3ms
const Koa = require('koa');
const app = new Koa();
app.use(async (ctx, next) => {
console.log(1);
// 這里是表示調(diào)用下一個中間件
await next();
console.log('1after');
const rt = ctx.response.get('X-Response-Time');
console.log(`${ctx.method} ${ctx.url} - ${rt}`);
});
app.use(async (ctx, next) => {
const start = Date.now();
console.log(start);
console.log(2);
await next();
console.log('2after');
const ms = Date.now() - start;
ctx.set('X-Response-Time', `${ms}ms`);
});
app.use(async ctx => {
console.log('3');
ctx.body = 'Hello World';
console.log('3after');
});
app.listen(3000);
const response = require('./response');
const context = require('./context');
const request = require('./request');
const Emitter = require('events');
class Application extends Emitter {
constructor() {
super();
this.context = Object.create(context);
this.request = Object.create(request);
this.response = Object.create(response);
this.middlewares = [];
}
use(middleware) {
this.middlewares.push(middleware);
// 鏈?zhǔn)秸{(diào)用
return this;
}
createContext(req, res) {
let ctx = Object.create(this.context);
let request = Object.create(this.request);
let response = Object.create(this.response);
ctx.request = request;
ctx.req = ctx.request.req = req;
ctx.response = response;
ctx.res = ctx.response.res = res;
return ctx;
}
// koa核心代碼
compose(middlewares) {
return function (ctx){
let index = -1;
const dispatch = (i) => {
if( i<= index){
return Promise.reject(new Error('next() called multiple times'))
}
index = i;
//多次調(diào)用next()函數(shù)
if (i === middlewares.length) return Promise.resolve();
let middleware = middlewares[i];
// 這里的dispatch(i + 1)就是next函數(shù)也就是調(diào)用下一個middleware中間件
return Promise.resolve(middleware(ctx, () => dispatch(i + 1)));
}
return dispatch(0);
}
}
handleRequest(req, res) {
let ctx = this.createContext(req, res); // 構(gòu)造上下文
this.compose(this.middlewares)(ctx).then(() => {
console.log('success');
}).catch(err=>{
})
}
listen() {
const server = http.createServer(this.handleRequest.bind(this));
server.listen(...arguments)
}
}
參考 https://juejin.cn/post/7214053344809861179
https://github.com/koajs/koa/blob/master/lib/application.js
https://www.ruanyifeng.com/blog/2016/09/redux_tutorial_part_two_async_operations.html