koa的洋葱模型是个啥哇?

377 阅读2分钟

1、是什么

Koa的洋葱模型是以next()函数为分割点,先由外到内执行Request的逻辑,然后再由内到外执行response的逻辑,这里的request逻辑,我们可以理解为值next之前的内容,response的逻辑是net函数之后的内容,也可以说每个中间件都有两次处理时机。它的核心原理主要是借助compose方法

68747470733a2f2f7261772e6769746875622e636f6d2f66656e676d6b322f6b6f612d67756964652f6d61737465722f6f6e696f6e2e706e67.png

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)
           }
       }
    }
    

4、参考