手把手和你一起学习Koa源码(三)——context&request&response

648 阅读4分钟

前言

本文的主要内容是通过描述作者自己学习koa源代码的过程,来和大家一起来学习koa的源码,koa设计的初衷是 致力于成为 web 应用和 API 开发领域中的一个更小、更富有表现力、更健壮的基石,说白了就是 小巧扩展性高所以koa的源码阅读起来也相对容易,如果你跟着我的文章学习 还学不会那么一定是我写的不好

在上一篇文章中我们学习了手把手和你一起学习Koa源码(二)——Appilication,了解到在listen方法中调用了自定义的 this.callback,在callback执行的过程中调用context、request、response,本文来看一下到底做了什么事情。

Context

Context的源码文件也只有252行,非常简单,整个看下来整个文件除了类型判断,错误处理之外最核心的两个地方就是cookies和delegate,本文主要就讲这两个部分。

cookies

先来看一下相关源码

const COOKIES = Symbol('context#cookies');
const Cookies = require('cookies');

  get cookies() {
    if (!this[COOKIES]) {
      this[COOKIES] = new Cookies(this.req, this.res, {
        keys: this.app.keys,
        secure: this.request.secure
      });
    }
    return this[COOKIES];
  },

  set cookies(_cookies) {
    this[COOKIES] = _cookies;
  }

这段代码主要是将cookies封装成get、set形式同时通过symbol的唯一特性创建了单例(这种写法也是非常常见好用,推荐大家在自己的代码中使用) 同时创建了Cookies对象,Cookies对象源码地址:github.com/ScottHamper… ,主要作用还是通过req对象获取到header中的cookie对象,以及可以对res对象的一些更改,具体大家可以自行看源码及用法。

delegate

/**
 * Response delegation.
 */

delegate(proto, 'response')
  .method('attachment')
  .method('redirect')
  .method('remove')
  .method('vary')
  .method('has')
  .method('set')
  .method('append')
  .method('flushHeaders')
  .access('status')
  .access('message')
  .access('body')
  .access('length')
  .access('type')
  .access('lastModified')
  .access('etag')
  .getter('headerSent')
  .getter('writable');

/**
 * Request delegation.
 */

delegate(proto, 'request')
  .method('acceptsLanguages')
  .method('acceptsEncodings')
  .method('acceptsCharsets')
  .method('accepts')
  .method('get')
  .method('is')
  .access('querystring')
  .access('idempotent')
  .access('socket')
  .access('search')
  .access('method')
  .access('query')
  .access('path')
  .access('url')
  .access('accept')
  .getter('origin')
  .getter('href')
  .getter('subdomains')
  .getter('protocol')
  .getter('host')
  .getter('hostname')
  .getter('URL')
  .getter('header')
  .getter('headers')
  .getter('secure')
  .getter('stale')
  .getter('fresh')
  .getter('ips')
  .getter('ip');

delegate的作用就是将request和response上的方法及属性 委派 给Context(源码中的proto指的就是Context)我们先将上面的代码缩减一下:

delegate(proto, 'response')
  .method('attachment')
  .access('status')
  .getter('headerSent')

主要调用了3个方法 method、access、getter,来看一下 delegate 到底做了什么事情,找到delegate的源码,先来看一下method

function Delegator(proto, target) {
  if (!(this instanceof Delegator)) return new Delegator(proto, target);
  
  // ① 获取proto和target
  this.proto = proto;
  this.target = target;
  
  // ② 创建methods、getters、setters、fluents数组
  this.methods = [];
  this.getters = [];
  this.setters = [];
  this.fluents = [];
}

Delegator.prototype.method = function(name){
  var proto = this.proto;
  var target = this.target;
  
  // ③ 将传入的方法名push到methods数组
  this.methods.push(name);

  // ④ 将传入的方法名挂载到proto身上,使proto调用该方法时可以直接调用proto[target]的方法
  proto[name] = function(){
    return this[target][name].apply(this[target], arguments);
  };

  return this;
};

调用delegator函数及method的方法主要完成了4件事

  • ① 获取proto(koa中指的就是context)和target (在koa中就是'request'或者'esponse')
  • ② 创建methods、getters、setters、fluents数组
  • ③ 将传入的方法名push到methods数组
  • ④ 将传入的方法名挂载到proto身上,使proto调用该方法时可以直接调用proto[target]的方法

我们再来看一下getter和access

Delegator.prototype.getter = function(name){
  var proto = this.proto;
  var target = this.target;
  this.getters.push(name);

  proto.__defineGetter__(name, function(){
    return this[target][name];
  });

  return this;
};

Delegator.prototype.setter = function(name){
  var proto = this.proto;
  var target = this.target;
  this.setters.push(name);

  proto.__defineSetter__(name, function(val){
    return this[target][name] = val;
  });

  return this;
};

Delegator.prototype.access = function(name){
  return this.getter(name).setter(name);
};

getter方法实现就是可以在ctx上直接调用request的属性(举个例子,ctx.host === ctx.request.host) setter方法实现的是可以在ctx上修改request的属性 access方法就是同时挂载getter和setter方法

koa就是通过 Delegator 这种模式 使得ctx 可以很方便的调用 request 以及 response 上面的方法及属性。

request&&response

request和response这两个对象其实也本质上也没什么特别值得说的地方,koa主要是将一些常用的方法和属性封装到两个对象中。 两个文件代码加一起一共1000多行,有一大半都是注释,个人建议这两个文件简单看一下,需要的时候完全可以当文档查一下,就不多赘述了。

总结

koa源码我们先分析到这里,本文完全按照作者自己学习源码的过程进行描述,文笔不好读起来可能会有一点流水账,但是作者会努力描述清楚,并且把阅读源码的一些方法技巧分享,请收藏点赞支持。

相关文章

手把手和你一起学习Koa源码(一)——目录结构手把手和你一起学习Koa源码(一)——目录结构 手把手和你一起学习Koa源码(二)——Appilication

本文使用 mdnice 排版