【Node】通过4个问题了解Koa原理

目录

1. 通过koa()创建了什么

2. app.listen()是如何启动服务器的

3. app.use(中间件) 中间件放到了哪里

4. 用户发送了请求,中间件是如何被回调的


1. 通过koa()创建了什么

// application.js 删除了部分代码
module.exports = class Application extends Emitter {
  constructor(options) {
    super();
    options = options || {};
    this.proxy = options.proxy || false;
    this.subdomainOffset = options.subdomainOffset || 2;
    this.proxyIpHeader = options.proxyIpHeader || 'X-Forwarded-For';
    this.maxIpsCount = options.maxIpsCount || 0;
    this.env = options.env || process.env.NODE_ENV || 'development';
    if (options.keys) this.keys = options.keys;
    this.middleware = [];
    this.context = Object.create(context);
    this.request = Object.create(request);
    this.response = Object.create(response);

    if (util.inspect.custom) {
      this[util.inspect.custom] = this.inspect;
    }
  }
};

创建了一个继承自events的Application对象,在constructor中进行了许多初始化操作。

2. app.listen()是如何启动服务器的

// application.js中的class Application中的类方法listen
  listen(...args) {
    debug('listen');
    const server = http.createServer(this.callback());
    return server.listen(...args);
  }

调用application.js中的class Application中的类方法listen,本质上还是调用了http.createServer。

callback方法在下面的“用户发送了请求,中间件是如何被回调的”中详细介绍。

3. app.use(中间件) 中间件放到了哪里

// application.js中的class Application中的类方法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;
  }

调用了application.js中的class Application中的类方法use,主要是将函数放入middleware这个数组中,等待被调用。

4. 用户发送了请求,中间件是如何被回调的

在listen中调用了callback函数。

// application.js中的class Application中的类方法listen
  listen(...args) {
    debug('listen');
    const server = http.createServer(this.callback());
    return server.listen(...args);
  }

让我们来看看callback方法。

// application.js中的class Application中的类方法callback
  callback() {
    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;
  }

将所有的中间件进行compose组合后传入handleRequest函数。

先来看看compose函数,它是从koa-compose中引入的。

// koa-compose/index.js
function compose (middleware) {
  if (!Array.isArray(middleware)) throw new TypeError('Middleware stack must be an array!')
  for (const fn of middleware) {
    if (typeof fn !== 'function') throw new TypeError('Middleware must be composed of functions!')
  }

  /**
   * @param {Object} context
   * @return {Promise}
   * @api public
   */

  return function (context, next) {
    // last called middleware #
    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]
      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)
      }
    }
  }
}

在这个函数中返回dispatch(0)的执行结果(第一个中间件是需要程序中自动执行的,因为没有前一个next);

在dispatch函数中首先根据传入的 i 获得第i个中间件(i从0开始),并通过Promise.resolve包裹;

若用户调用了next函数,则会调用dispatch函数。

return Promise.resolve(fn(context, dispatch.bind(null, i + 1)));

因此,compose函数主要的作用就是返回一个Promise,若用户调用了next函数,这个Promise会递归调用用户传入的中间件。

再让我们再回过头看一看callback函数中的handleRequest函数。

// application.js中的class Application中的类方法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所返回的Promise对象fnMiddleware。

以上,大概就是中间件被回调的过程。

参考资料:深入Node.js技术栈-学习视频教程-腾讯课堂

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值