1、是什么
Koa的洋葱模型是以next()函数为分割点,先由外到内执行Request的逻辑,然后再由内到外执行response的逻辑,这里的request逻辑,我们可以理解为值next之前的内容,response的逻辑是net函数之后的内容,也可以说每个中间件都有两次处理时机。它的核心原理主要是借助compose方法
api.use((ctx, next) => {
console.log('1')
await next();
console.log("5")
});
api.use((ctx, next) => {
console.log('2')
await next();
console.log("4")
})
api.use((ctx, next) => {
console.log('3')
})
// 顺序 1 2 3 4 5
2、为什么Koa使用洋葱模型
- 如果只链式执行一次,怎么能保证前面的中间件能够使用之后的中间件所添加的东西呢?如果只是链式执行一次,显然无法实现。
- 其次便是顺序问题
3、深入
-
middleware的源码,里面的数据类型是: this.midderware = []
-
app的listen函数:
创建服务的时候,传入了callback函数的返回值,
而callbak函数创建了一个函数,并将上下文ctx对象和下一个中间件传给当前的中间件,必须要等待下一个中间件执行完,再执行当前中间件的后续逻辑
listen(...args) { debug('listen'); // node http 创建一个服务 const server = http.createServer(this.callback()); return server.listen(...args); } callback() { // 返回值是一个函数 const fn = compose(this.middleware); const handleRequest = (req, res) => { // 创建 ctx 上下文环境 const ctx = this.createContext(req, res); return this.handleRequest(ctx, fn); }; return handleRequest; } -
compose函数
compose函数引用的是koa-compose这个库。
// 简易版的compose const middleware = [] let mw1 = async function (ctx, next) { console.log("next前,第一个中间件") await next() console.log("next后,第一个中间件") } let mw2 = async function (ctx, next) { console.log("next前,第二个中间件") await next() console.log("next后,第二个中间件") } let mw3 = async function (ctx, next) { console.log("第三个中间件,没有next了") } function use(mw) { middleware.push(mw); } use(mw1); use(mw2); use(mw3); let fn = function (ctx) { return dispatch(0) function dispatch(i) { const fn1 = middleware[i]; if (!fn1) return; return fn1(ctx, dispatch.bind(null, i+1)); } } fn(); // koa-compose源码 return function(context,next) { let index = -1 return dispatch(0) function dispatch(i) { if(i <= index) return Promise.reject(new Error('next() called multiple items')) index = i let fn = middleware[i] if(i === middleware.length) fn = next if(!fn) return Promise.resolve() try { return Promise.resolve(fn(context, dispatch.bind(null, i + 1))) } catch(err) { return Promise.reject(err) } } }