美文网首页
koa原理浅析

koa原理浅析

作者: 闪闪发光的狼 | 来源:发表于2020-06-13 10:22 被阅读0次
const Koa = require('koa');
const app = new Koa();
app.use(async ctx => {
  ctx.body = 'Hello World';
});
app.listen(3000);

一个简单的koa程序,我们看看它做了什么。
首先,惯例,打开koa的package.json,找到入口文件

 "main": "lib/application.js",

application.js

提供了koa的构造函数,基本是对http模块的封装。通过use函数收集中间件。

use(fn) {
    if (typeof fn !== 'function') throw new TypeError('middleware must be a function!');
    if (isGeneratorFunction(fn)) {
      deprecate('Support for generators will be removed in v3. ' +
                'See the documentation for examples of how to convert old middleware ' +
                'https://github.com/koajs/koa/blob/master/docs/migration.md');
      fn = convert(fn);
    }
    debug('use %s', fn._name || fn.name || '-');
    this.middleware.push(fn);
    return this;
  }

use将中间件存放在middleware数组。

callback() {  //createServer的回调函数
    const fn = compose(this.middleware);

    if (!this.listenerCount('error')) this.on('error', this.onerror);
    const handleRequest = (req, res) => {
      const ctx = this.createContext(req, res);
      return this.handleRequest(ctx, fn);
    };
    return handleRequest;
 }
handleRequest(ctx, fnMiddleware) {
    const res = ctx.res;
    res.statusCode = 404;
    const onerror = err => ctx.onerror(err);
    const handleResponse = () => respond(ctx);
    onFinished(res, onerror);
    return fnMiddleware(ctx).then(handleResponse).catch(onerror);
  }

可以看到核心就是compose(this.middleware)。

function compose (middleware) {
  return function (context, next) {
    let index = -1
    return dispatch(0) //开始执行中间件
    function dispatch (i) {
      if (i <= index) return Promise.reject(new Error('next() called multiple times'))
      index = i
      let fn = middleware[i] //获取中间件
      //fnMiddleware(ctx)没有传next参数,所以中间件执行完后fn为undefined
      if (i === middleware.length) fn = next 
      if (!fn) return Promise.resolve()
      try {
        //执行中间件。当用户执行next的时候,就是执行下一个中间件
        return Promise.resolve(fn(context, dispatch.bind(null, i + 1)));
      } catch (err) {
        return Promise.reject(err)
      }
    }
  }
}

再来回顾下koa的中间件

let app = new Koa();
app.use(async (ctx, next) =>{
    console.log(1);
    await next();
    console.log(4);
});
app.use(async (ctx, next) => {
    console.log(2);
    await next();
    console.log(3);
});

执行顺序是1,2,3,4 这是为什么呢。
1、执行1,废话。
2、next()。执行下一个中间件。打印2。
3、next()。后面没有中间件,直接打印3。
4、第二个中间件执行完毕。打印4。
这就是所谓的剥洋葱模型了。
再看看中间件是怎么被驱动的。
1、起始调用,fnMiddleware(ctx)。fnMiddleware是下面的函数

function (context, next){
  return dispatch(0) 
}

2、dispatch(0) 取出第一个中间件fn,并执行fn(context, dispatch.bind(null, i + 1)))。回顾下中间件

async (ctx, next) =>{
    console.log(1);
    await next();
    console.log(4);
}

next被赋值为dispatch(1)
3、函数里执行next(),即dispatch(1)。dispatch(1)就是执行下面函数

async (ctx, next) => {
    console.log(2);
    await next();
    console.log(3);
}

同理next被赋值为dispatch(2)
4、2等于中间件数组长度,next 变成了Promise.resolve().停止了中间件的遍历

//fnMiddleware(ctx)没有传next参数,所以中间件执行完后fn为undefined
if (i === middleware.length) fn = next 
if (!fn) return Promise.resolve()

context.js

这就是我们常用ctx。这个文件主要是做了代理的事。

delegate(proto, 'request')
  .method('acceptsLanguages')
  .access('idempotent')

request.js/response.js

对原生res,req进行了封装。

相关文章

网友评论

      本文标题:koa原理浅析

      本文链接:https://www.haomeiwen.com/subject/jglozhtx.html