什么是 中间件
在 JavaScript 中,中间件是指在应用程序的请求-响应周期中,位于请求和最终处理程序之间的函数。换句话说,中间件其实就是一个函数,一个执行特定逻辑的函数。
中间件的其中之一的作用
扩展(增强)函数的功能(从开始 ... 结束 中间要加入的部分)(eg:调用alert函数方法打印日志)
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提供了路由守卫 (router guards) 的机制,可以在路由导航过程中使用中间件来进行身份验证、日志记录等操作。
-
日志记录:中间件可以用于记录请求的信息,例如请求方法、URL、响应状态码等,以便后续分析和监控。
-
身份验证和授权:通过中间件,可以对请求进行身份验证和授权检查,以确保用户有权访问特定资源。
-
等等
如何实现一个中间件
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) {
// 执行当前中间件
// 传入的第二个参数即 `next()`,表示执行下一个中间件
await middleware(context, () => {
return runner(index + 1)
})
}
}
await runner(0)
}
}
// 创建一个中间件 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)
// 不调用 `next()`
})
pipeline.push((ctx, next) => {
console.log('当然,这个中间件不会被调用,这块消息也不会打印')
})
// 使用初始值 `{ value: 0 }`(即 `ctx` 的值)执行 pipeline
pipeline.execute({ value: 0 })
koa 中洋葱模型的实现
// 输出
// 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);
// 这里是表示调用下一个中间件
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);
// 链式调用
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;
//多次调用next()函数
if (i === middlewares.length) return Promise.resolve();
let middleware = middlewares[i];
// 这里的dispatch(i + 1)就是next函数也就是调用下一个middleware中间件
return Promise.resolve(middleware(ctx, () => dispatch(i + 1)));
}
return dispatch(0);
}
}
handleRequest(req, res) {
let ctx = this.createContext(req, res); // 构造上下文
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