美文网首页
Koa 源码分析

Koa 源码分析

作者: 梦想成真213 | 来源:发表于2019-10-16 16:46 被阅读0次

koa 的源码文件很少,lib 文件夹下只有4个文件 application、context、request、response。先看一下大概的调用关系:

在 package.json 文件中找到 koa 的入口文件 lib/application.js。下面我们进入 lib/application.js文件中看看一下源码的实现。

application

首先我们把代码精简一下,去掉无关的注释,以及一些验证处理,先找出实现的主流程逻辑:

导出的是 application 模块,这个模块继承了 events 类,然后是constrouctor里面挂载了很多的属性和方法。有三个重要对象是context,request,response,还有一个用来存储中间件函数的数组middleware。把无关代码先去掉,看最主要的:

const context = require('./context');
const request = require('./request');
const response = require('./response');

module.exports = class Application extends Emitter {
  constructor(options) {
    super();
    options = options || {};
    this.middleware = [];
    this.context = Object.create(context);
    this.request = Object.create(request);
    this.response = Object.create(response);
  }
}

context,request ,response 就是剩余的三个核心文件:上下文,请求和响应,暂且不看,继续往下。
到真正发起请求是调用 listen 方法,这方法封装了原始的 http 创建服务的请求:

const http = require('http');
listen(...args) {
    debug('listen');
    const server = http.createServer(this.callback());
    return server.listen(...args);
  }

listen 方法 http.createServer 传入this.callback(),this.callback 里面调到 this.createContext 来创建上下文,可以看到很多的原生的 Node 的 requestresponse 被挂到 context 对象上,这样可以直接使用ctx.resctx.req 的方式访问对象:

callback() {
    const fn = compose(this.middleware);
    const handleRequest = (req, res) => {
      const ctx = this.createContext(req, res);
      return this.handleRequest(ctx, fn);
    };
    return handleRequest;
  }
createContext(req, res) {
    const context = Object.create(this.context);
    const request = context.request = Object.create(this.request);
    const response = context.response = Object.create(this.response);
    context.app = request.app = response.app = this;
    context.req = request.req = response.req = req;
    context.res = request.res = response.res = res;
    request.ctx = response.ctx = context;
    request.response = response;
    response.request = request;
    context.originalUrl = request.originalUrl = req.url;
    context.state = {};
    return context;
  }

callback 函数最后返回了一个 handleRequest 函数,这个函数接收两个参数,原生 Node 中 http 模块的requestresponse,最后返回了 this.handleRequest 函数,这个函数接收上下文 ctx,和一个compose之后的 fn,这个 compose方法就是处理中间件的机制,这个中间件处理我们最后看。先把流程理清。

再看这个this.handleRequest 函数的实现,同样去掉一些错误的处理,代码实现就三行:

handleRequest(ctx, fnMiddleware) {
    const res = ctx.res;
    const handleResponse = () => respond(ctx);
    return fnMiddleware(ctx).then(handleResponse);
  }

this.handleRequest,拿到ctx.res,然后处理响应的结果,这个处理调了respond来处理响应,fnMiddleware(ctx)这个表示所有的中间件全部处理完成之后,统一处理响应response结果。再看看respond是如何实现的,同样去掉一些验证的处理,也就剩几行代码:

function respond(ctx) {
  if (Buffer.isBuffer(body)) return res.end(body);
  if ('string' == typeof body) return res.end(body);
  if (body instanceof Stream) return body.pipe(res);
  res.end(body);
}

可以看到这个响应返回的类型有三种:Buffer类型,字符串类型和stream类型
再看use方法,同样去掉干扰代码,也就两行:

 use(fn) {
    this.middleware.push(fn);
    return this;
  }

use中的参数就表示中间件函数,use方法就是收集所有的中间件函数放入到数组中,然后返回this,继续向下执行。middleware数组被传给compose库来处理中间件机制。
到这里主流程就结束了。

我们总结一下:创建服务createServert ->listen监听进来的请求 -> 处理中间件compose->响应结果

context、request、response

context 文件里面主要做一些request response 属性委托,让context本身具有这些属性,可以直接通过如ctx.url,ctx.body的方式设置获取数据,而不用通过ctx.request.url,ctx.response.body的方式。

request, response文件里面 主要是get set 的属性操作。

中间件机制compose

这个是引用的 koa-compose模块,这个模块用来处理koa的中间件数组。下面在node_modules里面找到koa-compose 模块,找到入口文件index.js,来看看里面是如何实现的。按照同样的方法,先去掉干扰代码,尽量干净,我们只看核心实现,可以看到实现非常的简洁:

module.exports = compose

function compose (middleware) {
  return function (context, next) {
    let index = -1
    return dispatch(0)
    function dispatch (i) {
      index = i
      let fn = middleware[i]
      if (i === middleware.length) fn = next
      return Promise.resolve(fn(context, dispatch.bind(null, i + 1)));
    }
  }
}

compose 函数传入中间件数组,就是application上的middleware,我们先写几个中间件函数,然后对应到代码里面的实现,这样更容易看些:

const app= new Koa()
const mid1 = async (ctx, next) => {
  ctx.body = 'hello';
  next();
}
const mid2 = async (ctx, next) => {
  ctx.body = ctx.body + ' world';
  next();
}
app.use(mid1)
app.use(mid2)

这时middleware = [mid1, mid2]现在调用listen方法,将参数往下传递,直到调用this.callback里面的fn = compose(middleware),可以调试代码,看fn到底是什么,执行fn.toString(),看到如下代码:

 function (context, next) {
    let index = -1
    return dispatch(0)
    function dispatch (i) {
      index = i
      let fn = middleware[i]
      if (i === middleware.length) fn = next
      return Promise.resolve(fn(context, dispatch.bind(null, i + 1)));
    }
  }

compose(middleware)返回一个函数,最主要的实现是 dispatch

function dispatch (i) {
      index = i
      let fn = middleware[i]
      if (i === middleware.length) fn = next
      return Promise.resolve(fn(context, dispatch.bind(null, i + 1)));
    }

this.callback返回this.handleRequest(ctx, fn); this.handleRequest调用fn(ctx),执行fn(ctx)首先return dispatch(0)return Promise.resolve(mid1(context, dispatch.bind(null, i + 1))),此时的 mid1next 就是 dispatch.bind(null, i + 1),调用next就是继续调用dispatch.bind(null, i + 1),索引加1继续往下一个中间件执行,程序运行到netx()的时候会暂停当前的程序运行,进入到下一个中间件,直到把middleware数组中的函数next()之前的代码都执行一遍之后,i === middleware.lengthfn = next,起初设置index = -1是为了将全部中间件函数里next()之前的部分执行完成之后,在从最后一项next()向上一个个穿过中间件函数执行各个中间件函数next()之后的代码,这个流程就是koa的洋葱模型:

总结来说Koa的重要实现有两点:
1. context的保存和传递; 2.中间件的管理和next的处理;

相关文章

  • 3.4 分析koa-static的源码

    本节我们将分析 koa-static 的源码。重点不在 熟记源码,而是掌握源码分析的方法。 如何看源码? 一般基于...

  • Koa 源码分析

    koa 的源码文件很少,lib 文件夹下只有4个文件 application、context、request、re...

  • 逐行分析Koa源码

    Koa 简介 官网介绍:Koa 是一个新的 web 框架,由 Express 幕后的原班人马打造, 致力于成为 w...

  • Koa源码分析(序)

    Koa是基于Node.js平台的下一代web框架。(在这里记录一下之前分析过的Koa)既然是基于Node.js,那...

  • Koa源码阅读

    Koa源码 继着上文的Co源码以及与Koa的深入理解,开始学习下Koa是怎么写的。我看的版本是1.1.2的。 从p...

  • Koa2源码分析

    源码结构 Koa的源码中主要为lib目录下的application.js, context.js, request...

  • 十分钟带你看完 KOA 源码

    前段时间看了 koa 源码,得益于 koa 良好抽象,不仅提供了简洁的 api ,同时也使得源码相当的简洁和优雅。...

  • 半小时掌握koa

    半小时掌握koa koa分析与实现从一个例子开始koa之流程app对象分析callback实现其他function...

  • koa2源码简单分析(三)

    接上篇分析,今天分析koa-compose这个库的源码,这个库的思想就是一个复合函数的思想,是将多个中间件函数合并...

  • koa源码

    koa 源码学习记录。因为代码量非常少,所以也很好理解。 http.createServer([requestLi...

网友评论

      本文标题:Koa 源码分析

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