手写koa2源码

一.node创建http服务

./create-node-server.js:

const http = require("http");
const server = http.createServer((req, res) => {
  res.end("hello node");
});

server.listen(3001, () => {
  console.log("server at 3001");
});

二.使用koa2创建服务

./index.js:

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

app.use(ctx => {
  ctx.body = {
    name: "Tony"
  };
});

app.listen(3000, () => {
  console.log("server at 3000");
});

三.手写koa初始源码

const http = require("http");
class Koa {
  constructor() {}
  use(callback) {
    this.callback = callback;
  }
  listen(...args) {
    // 创建服务
    const server = http.createServer((req, res) => {
      // 执行业务
      this.callback(req, res);
    });
    server.listen(...args);
  }
}
module.exports = Koa;


// 测试代码
const Koa = require("./koa");
const app = new Koa();

app.use((req, res) => {
  res.end("hello ");
});

app.listen(3000, () => {
  console.log("server at 3000");
});

四.引⼊上下⽂context

koa为了能够简化API,引⼊上下⽂context概念,将原始请求对象req和响应对象res封装并分别挂载到request和response上,再挂载到 context上,在context上设置getter和setter,从⽽简化操作。

封装响应对象req的部分内容:

./request.js:

module.exports = {
  get url() {
    return this.req.url;
  },
  get method() {
    return this.req.method.toLowerCase();
  }
};

封装响应对象res的部分内容:

./response.js:

module.exports = {
  get body() {
    return this._body;
  },
  set body(val) {
    this._body = val;
  }
};

将封装好的对象拉到context中:

./context.js

module.exports = {
  get url() {
    return this.request.url;
  },
  get method() {
    return this.request.method;
  },
  get body() {
    return this.response.body;
  },
  set body(val) {
    this.response.body = val;
  }
};

创建上下文时链接context、request、req、response、res。

在创建服务是创建上下文,将上下文传给业务,最后做出响应。

./koa.js:

const http = require("http");
const context = require("./context");
const request = require("./request");
const response = require("./response");

class Koa {
  constructor() {}
  use(callback) {
    this.callback = callback;
  }
  listen(...args) {
    // 创建服务
    const server = http.createServer((req, res) => {
      // 执行业务
      const ctx = this.createContext(req, res);
      this.callback(ctx);
      // 响应
      res.end(ctx.body);
    });
    server.listen(...args);
  }

  // 创建上下文方法
  createContext(req, res) {
    const ctx = Object.create(context);
    console.log(ctx);

    ctx.request = Object.create(request);
    ctx.response = Object.create(response);
    ctx.req = ctx.request.req = req;
    ctx.res = ctx.response.res = res;
  }
}
module.exports = Koa;

五、引入中间件

Koa中间件机制:Koa中间件机制就是函数式组合概念 Compose的概念,将⼀组需要顺序执⾏的 函数复合为⼀个函数,外层函数的参数实际是内层函数的返回值。洋葱圈模型可以形象表示这种机 制。

思路:在koa中使用use时,将传入的中间件放到中间件数组中,

在创建服务时将将多个中间件合并,并执行中间件,最后做出响应。

./koa.js:

const http = require("http");
const context = require("./context");
const request = require("./request");
const response = require("./response");

class Koa {
  constructor() {
    this.middlewares = [];
  }
  use(middleware) {
    // 将中间件添加到数组中
    this.middlewares.push(middleware);
  }
  listen(...args) {
    // 创建服务
    const server = http.createServer(async (req, res) => {
      // 执行业务
      const ctx = this.createContext(req, res);
      // 中间件合成
      const fn = this.compose(this.middlewares);
      // 执行合成函数并传入上下文
      await fn(ctx);
      // 响应
      res.end(ctx.body);
    });
    server.listen(...args);
  }

  // 创建上下文方法
  createContext(req, res) {
    const ctx = Object.create(context);
    ctx.request = Object.create(request);
    ctx.response = Object.create(response);
    ctx.req = ctx.request.req = req;
    ctx.res = ctx.response.res = res;
    // console.log("ctx1", ctx);
    return ctx;
  }

  // 合成函数
  compose(middlewares) {
    return function(ctx) {
      return dispatch(0);
      // 定义dispatch
      function dispatch(i) {
        let fn = middlewares[i];
        // 如果没有fn,则返回空promise
        if (!fn) {
          return Promise.resolve();
        }
        // 如果有fn,则返回执行fn的Promise
        return Promise.resolve(
          // 定义next函数
          fn(ctx, function next() {
            return dispatch(i + 1);
          })
        );
      }
    };
  }
}
module.exports = Koa;

测试代码:

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

const delay = () => new Promise(resolve => setTimeout(() => resolve(), 2000));
app.use(async (ctx, next) => {
  ctx.body = "1";
  await next();
  ctx.body += "5";
});
app.use(async (ctx, next) => {
  ctx.body += "2";
  await delay();
  await next();
  ctx.body += "4";
});
app.use(async (ctx, next) => {
  ctx.body += "3";
});

app.listen(3000, () => {
  console.log("server at 3000");
});

返回:12345

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值