koa包教包会系列(一) —— 白话koa

学习 koa 的必要性

koa 是由 express 原班人马打造的,小巧的,健壮的 web 开发框架,也正因为它这些的优点,eggjs 等很多 web 框架的核心也是由 koa 驱动的,熟悉 koa 代码,不仅对于使用 koa 进行开发有很大帮助,也有助于深入理解像 eggjs 等这样的,功能更加强大的框架。

知识复习

prototype 可以说是整个 javascript 的核心,是深入理解 javascript 关键知识点之一。
delegate 是一种编程方式,可以简化我们的操作。

1.1 首先声明“父类”(或子类的原型)

const Car = {
  get name() {
    return this.engine.name;
  },
  start: function() {
    return `${this.engine.name} start`;
  },
};
复制代码

1.2 使用用父类创建子类(父类在子类的原型链上)

const BMW = Object.create(Car);
复制代码

1.3 动态的给子类添加属性

BMW.engine = {
  name: 'v8',
};
复制代码

1.5 调用子类的方法

BMW.name; // 'v8'
BMW.start(); // 'v8 start'
复制代码

1.6 定义代理对象

const BMWPHONE = {};
复制代码

1.7 定义代理方法

function Delegate(source, target) {
  this.source = source;
  this.target = target;
}

Delegate.prototype.method = function(name) {
  const target = this.target;
  const source = this.source;
  source[name] = function() {
    return this[target][name].apply(this[target], arguments);
  };
};
复制代码

1.8 在代理对象上执行目标方法

const delegate = new Delegate(BMWPHONE, 'BMW');
delegate.method('start');
BMWPHONE.BMW = BMW;
BMWPHONE.start();
复制代码

定义了 Car 父类,BMW 继承了 Car 方法,并给 BMW 安装了牛逼的引擎,现在可以通过 BMW 手动启动了;
现在 BMW 开发一款手机,用手机操作代理手动启动。

之所以举上面的例子(这里你会体会到 js 和静态语言的巨大差异),是因为 koa 中 4 个文件中,有 3 个都是与原型和代理相关。

代码结构

koa 非常小巧,总共就 4 个文件,每个文件的功能也十分单一,文件名也清楚的反应了文件功能。

koa 的文件结构

├── application.js
├── context.js
├── request.js
└── response.js
复制代码
  • request.js

    主要针对 http 的 request 对象提供了改对象的大量的 get 方法,文件主要是用来获取 request 对象属性,参考 1.1。

  • response.js

    主要针对 http 的 response 对象提供了该对象的大量 set 方法;该文件主要是用来设置 response 对象属性,参考 1.1。

  • context.js

    koa 引入了上下文对象的概念,即 ctx,这里所谓的上下文对象实际上是 request 和 response 两个对象的并集,request 和 response 分别通过代理的形式,参考 1.8,将自己的方法委托给 ctx。那样我们就可以用 ctx 同时操作两个对象,来简化操作。

  • application.js

    该文件是整个 koa 的核心,简单来说主要有两大功能: 挂载真实请求到 ctx 下,封装中间件的执行顺序

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

这里 createContext 就是上小节 1.3 中的操作--继承原型链上的方法,给原型链上的方法准备数据。
这里做了很多冗余的挂载(冗余是为了兼容 express 部分写法),如果你只需要用 ctx,那么其中部分挂载可以省略。

module.exports = compose;
function compose(middleware) {
  return function(context, next) {
    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, function next() {
            return dispatch(i + 1);
          })
        );
      } catch (err) {
        return Promise.reject(err);
      }
    }
  };
}
复制代码

koa 中间件的特点是逆序执行,或者说是洋葱模型。以上 compose 方法就是实现洋葱模型的代码,上面方法简单来说就是: 将下个中间件的方法体,替换上个中间件的 next 参数。 compose 方法应该算是 koa 中最难理解的部分了。

app.use(async function1(ctx, next) {
  console.log(1.1);
  next();
  console.log(1.2);
})
app.use(async function2(ctx, next) {
  console.log(2.1);
  next();
  console.log(2.2);
})
复制代码

经过 compose 函数处理后,实际执行代码可以看做:

(async function1(ctx) {
  console.log(1.1);
    // next 部分
    (async function2(ctx) {
        console.log(2.1);
          ...
        console.log(2.2)
      }
    )(ctx)
  conosle.log(1.2);
})(ctx)
复制代码

那么可以看出洋葱模型的特点: 先注册的中间件,位于模型的最外侧
实际上 koa 主要做了两件事情:

  1. 封装 ctx
  2. 组装中间件,实现逆序执行的洋葱模型

总结

与 hapi,eggjs 比起来,koa 真的十分小巧,以至于不能称作一种框架,可以看做一种库,但这并不妨碍 koa 生态的发展。

express 当初也是大而全的框架,慢慢的把各种功能已中间件的形式抽离出来,koa 可以看做这种思想的一种实现。大而全的框架主要存在起初的学习成本高,功能冗余等问题,使用 koa 对于初次使用 nodejs 开发 web 的人员非常友好,但也存在一定问题,过于灵活编程,导致编程方式千差万别,没有一个统一的标准,不同工程代码之间学习成本高。

对于初学者来说,建议从 koa 入手,使用不同的中间件来实现不同的功能,对于了解 web 开发有很大帮助。当有一定积累和经验后,可以自己约定 koa 编程规则,当自己编程规则无法满足需求时,就可以入手真正的框架了。


关注微信公众号:创宇前端(KnownsecFED),码上获取更多优质干货!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值