前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >深入koa源码 - 手动实现玩具版koa

深入koa源码 - 手动实现玩具版koa

作者头像
心谭博客
发布2020-04-20 17:15:24
3790
发布2020-04-20 17:15:24
举报
文章被收录于专栏:YuanXinYuanXin

最近读了 koa2 的源码,理清楚了架构设计与用到的第三方库。本系列将分为 3 篇,分别介绍 koa 的架构设计和 3 个核心库,最终会手动实现一个简易的 koa。这是系列第 3 篇,模拟实现玩具版 koa

源码和测试代码放在了:dongyuanxin/simple-koa

准备

设计思想和第三方库原理都在前 2 篇详细说明了。这篇主要目的是做一个验证检验,在语法使用 ES6/7 的语法。

在开始前,安装一下需要用到的库:

代码语言:javascript
复制
npm install --save koa-compose koa-convert is-generator-function

测试文件

为了说明效果,先按照正常使用 koa 的逻辑编写了测试文件。当启动它的时候,它的预期行为是:

  • 监听 3000 端口
  • 加载中间件
  • 浏览器访问localhost:3000,屏幕打印hello
  • 服务器的控制台依次输出:1inner => 2innter => 2outer => 1outer

代码如下:

代码语言:javascript
复制
const Koa = require("./lib/application");

const server = new Koa();

async function middleware1(ctx, next) {
    console.log("1 inner");
    await next();
    console.log("1 outer");
}

async function middleware2(ctx, next) {
    ctx.res.body = "hello";
    console.log("2 inner");
    await next();
    console.log("2 outer");
}

server.use(middleware1);
server.use(middleware2);

server.listen(3000);

玩具 koa

只准备了一个文件,跑通上面的逻辑即可。文件是 lib/application.js

构造函数

首先对外暴露的就是一个继承 Emitter 的 Application 类。整体框架如下:

代码语言:javascript
复制
const http = require("http");
const Emitter = require("events");
const compose = require("koa-compose");

module.exports = class Application extends Emitter {
    constructor() {
        super();

        this.middleware = []; // 中间件
        this.context = {}; // 上下文
        this.request = {}; // 请求信息
        this.response = {}; // 返回信息
    }

    listen(...args) {}

    use(fn) {}

    callback() {}

    handleRequest(ctx, fnMiddleware) {}

    createContext(req, res) {}

    onerror(error) {
        console.log(`error occurs: ${error.message}`);
    }
};

继承 Emitter 事件类,是为了方便监听和处理报错。

use

将外面传入的中间件保存起来:

代码语言:javascript
复制
use (fn) {
  this.middleware.push(fn)
  return this
}

createContext

主要用于创建上下文。外面可以通过访问 ctx 上的 req/res 拿到请求或者返回信息。

代码语言:javascript
复制
createContext (req, res) {
  const context = Object.create(this.context)
  context.request = Object.create(this.request)
  context.response = Object.create(this.response)
  context.req = req
  context.res = res

  context.app = this
  context.state = {}

  return context
}

listen 和 callback

监听端口,启动服务器:

代码语言:javascript
复制
listen (...args) {
  const server = http.createServer(this.callback())
  return server.listen(...args)
},
callback () {
  const fn = compose(this.middleware)
  this.on('error', this.onerror)

  return (req, res) => {
    const ctx = this.createContext(req, res)
    return this.handleRequest(ctx, fn)
  }
}

handleRequest

callback 方法中真是返回的内容,它的作用就是:处理请求,并且返回给客户端。

代码语言:javascript
复制
handleRequest(ctx, fnMiddleware) {
  const res = ctx.res
  // res.statusCode = 404
  const handleResponse = () => {
    res.end(res.body)
  }

  return fnMiddleware(ctx)
    .then(handleResponse)
    .catch(this.onerror)
}

效果截图

启动 index.js 后,在浏览器访问本地 3000 端口:

回到控制台,查看中间件的输出顺序是否正确:

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019-06-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 准备
  • 测试文件
  • 玩具 koa
    • 构造函数
      • use
        • createContext
          • listen 和 callback
            • handleRequest
            • 效果截图
            相关产品与服务
            消息队列 TDMQ
            消息队列 TDMQ (Tencent Distributed Message Queue)是腾讯基于 Apache Pulsar 自研的一个云原生消息中间件系列,其中包含兼容Pulsar、RabbitMQ、RocketMQ 等协议的消息队列子产品,得益于其底层计算与存储分离的架构,TDMQ 具备良好的弹性伸缩以及故障恢复能力。
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档