Node.js koa源码浅析

简介

Koajs
www.koajs.com.cn

优势

Koa 是一个轻量级的、极富表现力的 http 框架。
一个web request会通过 Koa 的中间件栈,来动态完成 response 的处理。
Koa2 采用了 async 和 await 的语法来增强中间件的表现力。
Koa 不在内核方法中绑定任何中间件,它仅仅提供了一个轻量优雅的函数库。

带着问题

1、koa洋葱模型怎么实现的?
2、怎么启动(初始化),做了那些事情?
3、一个请求进来如何处理的流程?

koa github
github.com/koajs/koa/t…

koa初始化

1、new Koa 初始化一个实例

// app.js
const Koa = require('koa');

// 1初始化一个Koa
const app = new Koa();
复制代码

2、app.use搜集中间件到middleware数组

// app.js
// 搜集中间件到middleware数组
// x-response-time
app.use(async (ctx, next) => {
  const start = Date.now();
  await next();
  const ms = Date.now() - start;
  ctx.set('X-Response-Time', `${ms}ms`);
});

// logger
app.use(async (ctx, next) => {
  const start = Date.now();
  await next();
  const ms = Date.now() - start;
  console.log(`${ctx.method} ${ctx.url} - ${ms}`);
});

// response
app.use(async ctx => {
  ctx.body = {};
});
复制代码

3、app.listen 合成中间件fnMiddleware,返回一个callback函数给http.createServer,开启服务器,等待http请求。

  • koa application里的响应回调。

image.png
中间件用来处理请求流程
koa-compose github.com/koajs/compo…


// middleware [fn1, fn2, fn3]
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
      // fn就是当前的中间件
      let fn = middleware[i]
      if (i === middleware.length) fn = next // 最后一个中间件如果也next时进入(一般最后一个中间件是直接操作ctx.body,并不需要next了)
      if (!fn) return Promise.resolve() // 没有中间件,直接返回成功
      try {
        
        /* 
          * 使用了bind函数返回新的函数,类似下面的代码
          return Promise.resolve(fn(context, function next () {
            return dispatch(i + 1)
          }))
        */
        // dispatch.bind(null, i + 1)就是中间件里的next参数,调用它就可以进入下一个中间件
        // fn如果返回的是Promise对象,Promise.resolve直接把这个对象返回
        // fn如果返回的是普通对象,Promise.resovle把它Promise化
        return Promise.resolve(fn(context, dispatch.bind(null, i + 1)));
      } catch (err) {
        return Promise.reject(err)
      }
    }
  }
}
复制代码
// 文件位置 ./node_modules/koa/lib/application.js
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;
}

listen(...args) {
  debug('listen');
  const server = http.createServer(this.callback());
  return server.listen(...args);
}
复制代码
// app.js
// listen
app.listen(3000);
console.log('app', Koa);
复制代码

image.png

处理请求流程

每次请求,createContext生成一个新的ctx,传给fnMiddleware,触发中间件的整个流程

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);
}
复制代码

总结

图片[3]-Node.js koa源码浅析-一一网Koa是一个设计非常精简的Web框架(库),源代码本身不含任何中间件,可以使我们根据自身需要去组合一些中间件使用,也可以自身去开发一些中间件。它结合async/await实现了洋葱模式。

问题

koa的洋葱圈模型怎样实现的?

// 这样就可能更好理解了。
// simpleKoaComposeconst 
[fn1, fn2, fn3] = this.middleware;
const fnMiddleware = function(context) {
    return Promise.resolve(fn1(context, 
    function next() {
        return Promise.resolve(fn2(context,
        function next() {
            return Promise.resolve(fn3(context,
            function next() {
                return Promise.resolve();
            }))
        }))
    }));
};
fnMiddleware(ctx).then(handleResponse).catch(onerror);
复制代码

app.use() 把中间件函数存储在middleware数组中,

最终会调用koa-compose导出的函数compose返回一个promise,中间函数的第一个参数ctx是包含响应和请求的一个对象,会不断传递给下一个中间件。next是一个函数,返回的是一个promise。 最后一个中间件调next 直接返回Promise.resolve();

a, b, c 三个中间件
return a(ctx,b(ctx,c(ctx, next))

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享