Koa 的模块依赖关系简介

阅读本文预计花费 6 分钟

------_173846f0-43c6-46f1-89e4-80e49034b6ab

上图整理的是一张简单的依赖关系图,以 application.js 为入口,依赖了 context.js、request.js、response.js 以及几个 Node 模块和一堆三方模块。

可以看到 Koa 主要是基于 Node http 模块实现,也就是说 Koa 是一个 HTTP Server 端框架「其实 Koa 将来可能会进一步抽象以适应各种场景需求,ths is an issue」。

本文主要通过依赖来了解 Koa,那么接下来我们就分三部分来看看主要的依赖模块。

Node 标准模块

net

The net module provides an asynchronous network API for creating stream-based TCP or IPC servers (net.createServer()) and clients (net.createConnection()).

该模块主要是赋予了 Node 应用网络的能力,提供了一套基于流的 TCP/IPC 网络 API。「net module

http

To use the HTTP server and client one must require('http').
node 网络

如上图,基于 tls、http 模块还实现了 https 模块。

http 模块是基于 net 模块对 HTTP 应用层协议的实现,提供了针对 HTTP 相关的一系列 API。「http module

events

The EventEmitter class is defined and exposed by the events module

什么是 EventEmitter 呢?简单来说,使用 EventEmitter,可以监听一个事件,并且可以执行一个已经绑定的回调函数。
这实质上是实现了发布/订阅者模式,就像 Web 页面里使用 addEventListener 监听用户点击事件一样。Koa 的 application 就是继承自 EventEmitter,因此 每个 Koa 实例都可以被订阅以及发布事件。Koa 中其实就通过发布/订阅处理了内部的 error。

stream

A stream is an abstract interface for working with streaming data in Node.js. The stream module provides a base API that makes it easy to build objects that implement the stream interface.

了解 Node Stream 可以参考 深入理解 Node.js Stream 内部机制
Koa 依赖 Stream 模块是为了能处理流类型的响应数据:

// responses
if (Buffer.isBuffer(body)) return res.end(body);
if ('string' == typeof body) return res.end(body);
if (body instanceof Stream) return body.pipe(res);

// body: json
body = JSON.stringify(body);
if (!res.headersSent) {
ctx.length = Buffer.byteLength(body);
}
res.end(body);

剩下的三个标准模块「util、url、path」可以理解成工具类吧,具体使用可以查文档看 API。

三方模块

Koa 不仅仅指单个仓库里的代码。在整个迭代过程中,一些可复用、抽象的逻辑都被提取成一个三方包「对于 Koa,称为二方包更合适点」,这些包供 Koa 使用的同时,还适用于其他场景。当然 Koa 也依赖了不少三方模块「站在巨人的肩膀,看到高望得远嘛」。

koa-compose

Koa 中间件实现洋葱模型的核心模块,其实就 20 几行代码。

简化源码「删除入参判断、注释、空格.etc」后,如下:

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
      if (!fn) return Promise.resolve()
      try {
        return Promise.resolve(fn(context, function next () {
          return dispatch(i + 1)
        }))
      } catch (err) {
        return Promise.reject(err)
      }
    }
  }
}

这个模块就导出了一个函数,其接收一个数组 middleware(竟然没有 s),返回一个新的函数「有两个入参,context、next」。
这坨代码核心是那个 dispatch 函数,其内部是个递归。洋葱模型的中间件实现在很多业务中都有适用场景,这大概就是 koa-compose 模块的存在原因吧。

only

模块做的事情比较简单但是实用,它可以让你得到对象中指定的几个属性组成的对象。

var obj = {
  name: 'tobi',
  last: 'holowaychuk',
  email: 'tobi@learnboost.com',
  _id: '12345'
};
 
var user = only(obj, 'name last email');
// {
//   name: 'tobi',
//   last: 'holowaychuk',
//   email: 'tobi@learnboost.com'
// }

可以通过空格将所需的属性拼接成字符串传入或者传入一个数组['name', 'last', 'email']
举个常用的场景:响应一个请求时,数据库查询结果冗余,那完全可以通过 only 模块简化再返回给客户端「当然你写的 SQL 恰到好处就当我没说」。

delegates

顾名思义,delegates 就是起到代理的作用。它主要实现的是一个对象实例对其属性的属性的代理。
Koa 中主要是为了让 context 实例能方便的访问其挂载着的 request、response 中的属性、行为。

delegate(ctx, 'request')
  .access('url');

这样开发者就可以自己通过 ctx.urlctx.request.url 读写,极大了方便了广大开发者。可能这就是 Koa 成为成功的开源框架的原因之一吧。

「jshttp」

Low-Level JavaScript HTTP-related Modules

jshttp 不是一个模块,而是一个开源组织。其主要开发 HTTP 相关的基础模块,如常用的 cookie、methods 等模块。
Koa 中大量依赖了 jshttp 开发的各种基础模块。当然这些模块在实际业务代码中也是有用武之地,比如 statuses、http-errors .etc。

小结

本文主要简单地介绍了 Koa 的一些依赖关系以及一些重要模块的简单介绍。希望看官们能有所收获,文中如有纰漏,望不吝斧正。

知识共享许可协议
本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。