likes
comments
collection
share

手把手带你造轮子系列之Koa篇(三)

作者站长头像
站长
· 阅读数 50

手把手带你造轮子系列之Koa篇(三)

概览:Koa是一个基于Node.js平台的下一代Web框架,Express的原班人马打造, 旨在为现代Web应用程序提供更有表达力更健壮的支撑。Koa的核心思想是使用ES6中的Async/Await语法解决异步流程控制问题,并采用了独特的中间件机制来处理请求和响应,使得Koa非常适合用于编写高效简洁、灵活、可扩展、易维护的Web应用程序。

此篇是Koa系列的第三篇,也是Koa系列的终篇。上一篇我们实现了MiniKoa的中间件系统,对主体流程做了调整,重点实现了compose方法,其实到这一步Koa就已经能够正常使用了,我们需要做的就是继续完善,让它更好用,今天主要扩充下context上下文和封装请求和响应的一些常用的属性和方法。 我们先创建三个文件context.js、request.js、response.js分别完善上下文、请求和响应的的功能,然后在Application.js中使用上面三个文件

修改Application.js

为了监听和触发响应事件,我们让Application继承Nodejs的内置模块events

const Emitter = require('events');
const http = require("http"); 
const response = require('./response');
const context = require('./context');
const request = require('./request');
module.exports = class Application extends Emitter { 
   constructor() { 
   super();
   this.context = Object.create(context);
   this.request = Object.create(request);
   this.response = Object.create(response); 
  } 
};

大家看到我们没有直接赋值,而是用Object.create方法继承,这样的好处就是不同的应用他们之间的上下文是相互隔离的,不受影响,这点很值得大家学习。

封装request对象

封装了一些常用的请求的方法和属性:


module.exports = {
   // 返回请求头
   get header() {
       return this.req,header
   },
   // 设置请求头
   set header(val) {
       this.req.headers = val;
  },
  // 不一一列举了,总是是添加了一些属性,和方法
  ...
};

封装response对象

封装了一些常用的响应的方法和属性:

module.exports = {
// 返回请求的socket
 get socket() {
    return this.res.socket;
  },
  // 返回响应头
  get header() {
    const { res } = this;
    return typeof res.getHeaders === 'function'
      ? res.getHeaders()
      : res._headers || {}; // Node < 7.7
  }
 // ...同request,扩展了一些属性方法
}

接下来我们要思考一个问题,request和response都是在原始的http的req和res上进行扩展的,所以我们要能够通过this取到req和res,因为用户用的时候都是通过context去调用response和request,所以我们在创建context的时候要将req和res挂载到上context上,这样就可以取到进而进行功能的扩展,接下来我们要修改createContext方法来达成次目标

修改createContext

 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;
    return context;
}

大家发现body的获取和设置被移除了,那是因为,封装进了response对象里面。同样我们需要用Object.create去继承,这样可以保证每一次用户请求的变量是相互隔离的,然后进行了一系列的赋值,目的就是讲res和req挂载到context上。在Koa中,所有的功能最终都会提供到context上,统一了用户的操作,除了扩展的request和response,也保留的原生的req和res。但是这样又引出一个问题,就是我们去使用的时候不方便,比如我使用response上的某个方法时需要context.response.xxx,request同样存在这个问题,下面我们就来解决这个问题

封装context对象:

封装了一些context自身的属性和方法:

const delegate = require('delegates');
module.exports = {  
  inspect() {
    if (this === proto) return this;
    return this.toJSON();
  },
  // ... context自身的一些属性和方法,同样就不一一列举了 
}
delegate(proto, 'response').method('xxx').access('xxx').getter('xxx')
delegate(proto, 'request').method('xxx').access('xxx').getter('xxx')

对于context的封装同样是添加了一些属性方法,然后通过一个库delegate对response和request的方法进行了代理,也就是说我们可以直接通过context去调用reqest和response的方法,因为delegate会进行代理,代理到response或者request上。这样就完美的解决了我们的问题。具体delegate的实现我就不展开介绍了,如果有小伙伴感兴趣可以在评论区交流。

本篇完添加了request、response和context的功能,至此,我们的MiniKoa就基本完成了,感兴趣的小伙伴可以通过这三篇的介绍自己动手实现下Koa的核心特性和功能,相信一定会非常有收获,期间还有很多细节,后面如果大家感兴趣我再和大家分享。

转载自:https://juejin.cn/post/7203377276558278714
评论
请登录