前言
本文的主要内容是通过描述作者自己学习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 排版