天天看点

理解 Koa 框架中间件原理

原文链接

Node 主要用在开发 Web 应用,koa 是目前 node 里最流行的 web 框架。

在 Node 开启一个 http 服务简直易如反掌,官网 demo。

const http = require("http");

const server = http.createServer((req, res) => {
  res.statusCode = 200;
  res.setHeader("Content-Type", "text/plain");
  res.end("Hello World\n");
});

const hostname = "127.0.0.1";
const port = 3000;
server.listen(port, hostname, () => {
  console.log(`Server running at http://${hostname}:${port}/`);
});
复制代码
           
  • 引入

    http

    模块,

    http

    createServer

    方法创建了一个

    http.Server

    的实例。
  • server

    监听

    3000

    端口。
  • 我们传入到

    createServer

    里的函数实际是监听

    request

    事件的回调,每当请求进来,监听函数就会执行。
  • request

    事件的监听函数,其函数接受两个参数,分别是

    req

    res

    。其中

    req

    是一个可读流,

    res

    是一个可写流。我们通过

    req

    获取

    http

    请求的所有信息,同时将数据写入到

    res

    来对该请求作出响应。

koa 应用

koa 如何创建一个 server, 直接上个官网的例子

const Koa = require("koa");
const app = new Koa();

// 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 = "Hello World";
});

app.listen(3000);
复制代码
           

中间件概念在编程中使用广泛, 不管是前端还是后端, 在实际编程中或者框架设计都有使用到这种实用的模型。

基本上,Koa 所有的功能都是通过中间件实现的。

每个中间件默认接受两个参数,第一个参数是

Context

对象,第二个参数是

next

函数。只要调用

next

函数,就可以把执行权转交给下一个中间件。

如果中间件内部没有调用

next

函数,那么执行权就不会传递下去。

多个中间件会形成一个栈结构(middle stack),以“先进后出”(first-in-last-out)的顺序执行。整个过程就像,先是入栈,然后出栈的操作。

上面代码的执行顺序是:

请求 ==> x-response-time 中间件 ==> logger 中间件 ==> response中间件 ==> logger 中间件 ==> response-time 中间件 ==> 响应
复制代码
           

理解 Koa 的中间件机制(源码分析)

阅读源码,化繁为简,我们看看 koa 的中间件系统是如何实现的。

class Application extends Emitter {
  constructor() {
    super();
    this.middleware = [];
  },

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

  callback() {
    const fn = compose(this.middleware);

    return function(req, res) {
      return fn(ctx);
    };
  },

  listen(...args) {
    const server = http.createServer(this.callback());
    return server.listen(...args);
  }
}
复制代码
           

好了,精简结束,一不小心,去枝末节,最后只剩下不到 20 行代码。

这就是框架的核心,简化后的代码非常清晰,有点不可思议,但核心就是这么简单。

我们先分析以上代码做了什么事。

  • 我们定义了一个

    middleware

    数组来存储中间件。
  • 我们定一个了一个

    use

    方法来注册一个中间件。其实就是简单的

    push

    到自身的

    mideware

    这个数组中。
  • 我们还使用了一个

    compose

    方法,传入

    middleware

    ,应该是做了一些处理,返回了一个可执行的方法。

你一定对中间的

compose

方法很好奇,初此之外的代码都容易理解,唯独这个

compose

不太知道究竟做了什么。

其实,

compose

就是整个中间件框架的核心。

compose

之外,代码已经很清楚的定义了

  • 中间件的存储
  • 中间件的注册

compose

方法做了最为重要的一件事

  • 中间件的执行

核心源码

compose

先上码

function compose(middleware) {
  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);
      }
    }
  };
}
复制代码
           

我试图去简化一下这个方法,但方法本身已经足够简洁。

代码很简洁。

通过

next()传递

实现中间件调用, 结合

Promise

采用 递归调用 的通知机制。

看图

理解 Koa 框架中间件原理

这种形式的控制流让整个 Koa 框架中间件的访问呈现出

自上而下的中间件流

+

自下而上的 response 数据流

的形式。

Koa 本身做的工作仅仅是定制了中间件的编写规范,而不内置任何中间件。一个 web

request

会通过 Koa 的中间件栈,来动态完成

response

的处理。

koa 在中间件语法上面采用了

async

+

await

语法来生成

Promise

形式的程序控制流。

总结

koa 是非常精简的框架, 其中的精粹思想就是洋葱模型(中间件模型), koa 框架的中间件模型非常好用并且简洁, 但是也有自身的缺陷, 一旦中间件数组过于庞大, 性能会有所下降,我们需要结合自身的情况与业务场景作出最合适的选择.

作者:梁仔

链接:https://juejin.im/post/5bc48ba5f265da0aaa053e17

来源:掘金

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。