Koa源码阅读-详解
承接上一章
Application函数
module.exports = class Application extends Emitter { constructor() { super(); this.proxy = false; this.middleware = []; this.subdomainOffset = 2; this.env = process.env.NODE_ENV || 'development'; // context,request,response是空对象,能访问相应原型对象上的属性和方法 this.context = Object.create(context); this.request = Object.create(request); this.response = Object.create(response); } }
Emitter
Application继承自Emitter
Emitter主要是观察订阅者模式,在index.js可以这样写,会出现结果
const app = new Koa(); app.on('a',(element)=>{ // 111 console.log(element) }) app.emit('a',111)
学习一下Object.create
const a = {a:1,b:2} const b = Object.create(a); // {} console.log(b.__proto__ === a) //true console.log(b.a) //1
a在b的原型链上,b能访问a的属性
单元测试
toJSON() { return only(this, [ 'subdomainOffset', 'proxy', 'env' ]); } inspect() { return this.toJSON(); }
only函数返回新对象,并且只返回包含key,key的值不为null
module.exports = function(obj, keys){ obj = obj || {}; if ('string' == typeof keys) keys = keys.split(/ +/); return keys.reduce(function(ret, key){ if (null == obj[key]) return ret; ret[key] = obj[key]; return ret; }, {}); };
//执行app.inspect() describe('app.inspect()', () => { it('should work', () => { const app = new Koa(); const util = require('util'); const str = util.inspect(app); // 相等则通过 assert.equal("{ subdomainOffset: 2, proxy: false, env: 'test' }", str); }); });
这里有个疑问,为什么在index.js 实例后的对象也只有这3个属性???
listen
调用listen执行callback
listen() { debug('listen'); const server = http.createServer(this.callback()); return server.listen.apply(server, arguments); } callback() { const fn = compose(this.middleware); if (!this.listeners('error').length) this.on('error', this.onerror); const handleRequest = (req, res) => { res.statusCode = 404; const ctx = this.createContext(req, res); const onerror = err => ctx.onerror(err); const handleResponse = () => respond(ctx); onFinished(res, onerror); fn(ctx).then(handleResponse).catch(onerror); }; return handleRequest; }
在这里顺便说一下,原生 http创建一个服务是这样子滴
const http = http.createServer((req, res) => { res.writeHead(200, { 'Content-Type': 'text/plain' }); res.end('okay'); }); http.listen(8000)
createContext
createContext(req, res) { // 以this.context为蓝本建立对象,此时引入的this.context已经两次实例了。request和response同理,并且作为属性挂在context上 const context = Object.create(this.context); const request = context.request = Object.create(this.request); const response = context.response = Object.create(this.response); // context request response都能拿到this,req,res context.app = request.app = response.app = this; context.req = request.req = response.req = req; context.res = request.res = response.res = res; // request response有context; request.ctx = response.ctx = context; // request和response能互相拿到各自的对象 request.response = response; response.request = request; // context 和 request的originalUrl有了req.url context.originalUrl = request.originalUrl = req.url; // context能拿到cookie context.cookies = new Cookies(req, res, { keys: this.keys, secure: request.secure }); request.ip = request.ips[0] || req.socket.remoteAddress || ''; context.accept = request.accept = accepts(req); context.state = {}; // 返回 context return context; }
respond
经过中间件的处理,respond
function respond(ctx) { // allow bypassing koa if (false === ctx.respond) return; const res = ctx.res; if (!ctx.writable) return; //拿到body let body = ctx.body; const code = ctx.status; // 如果body不存在,那么返回默认not found // status body if (null == body) { body = ctx.message || String(code); if (!res.headersSent) { ctx.type = 'text'; ctx.length = Buffer.byteLength(body); } return res.end(body); } // 如果body是二进制,是字符串,Stream流,直接返回 // responses if (Buffer.isBuffer(body)) return res.end(body); if ('string' == typeof body) return res.end(body); if (body instanceof Stream) return body.pipe(res); // body: json json的处理 body = JSON.stringify(body); if (!res.headersSent) { ctx.length = Buffer.byteLength(body); } res.end(body); }
context函数
前面主要做了onerror和暴露属性的处理
来看最重要的
/** * context能够使用response下attachment redirect的方法, * 能够设置respons下status message的属性, * 能读headerSent和writable的值 */ delegate(proto, 'response') .method('attachment') .method('redirect') .method('remove') .method('vary') .method('set') .method('append') .method('flushHeaders') .access('status') .access('message') .access('body') .access('length') .access('type') .access('lastModified') .access('etag') .getter('headerSent') .getter('writable');
function Delegator(proto, target) { // 调用函数即返回实例后的自己,每调用一次都是不同的this,好处能够调用原型对象上的方法 if (!(this instanceof Delegator)) return new Delegator(proto, target); this.proto = proto; this.target = target; this.methods = []; this.getters = []; this.setters = []; this.fluents = []; }
// 把target的上的方法挂载在proto上,而执行的this却是target的 Delegator.prototype.method = function(name){ var proto = this.proto; var target = this.target; this.methods.push(name); proto[name] = function(){ return this[target][name].apply(this[target], arguments); }; return this; };
proto[name]能读取target[name]
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; };
proto[name]能设置target[name]
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); };
request对象
一句话,request对象主要做了拿到请求的信息
我们看几个例子
// 返回this.req.headers信息 get header() { return this.req.headers; }, get(field) { const req = this.req; switch (field = field.toLowerCase()) { case 'referer': case 'referrer': return req.headers.referrer || req.headers.referer || ''; default: return req.headers[field] || ''; } }, // 获取请求头的内容铲毒 get length() { const len = this.get('Content-Length'); if (len == '') return; return ~~len; }, //获得query信息 get querystring() { if (!this.req) return ''; return parse(this.req).query || ''; }
response对象
response对象主要做了相应头的的信息
set(field, val) { if (2 == arguments.length) { if (Array.isArray(val)) val = val.map(String); else val = String(val); this.res.setHeader(field, val); } else { for (const key in field) { this.set(key, field[key]); } } }, set lastModified(val) { if ('string' == typeof val) val = new Date(val); this.set('Last-Modified', val.toUTCString()); }, set etag(val) { if (!/^(W\/)?"/.test(val)) val = `"${val}"`; this.set('ETag', val); } redirect(url, alt) { // location if ('back' == url) url = this.ctx.get('Referrer') || alt || '/'; this.set('Location', url); // status if (!statuses.redirect[this.status]) this.status = 302; // html if (this.ctx.accepts('html')) { url = escape(url); this.type = 'text/html; charset=utf-8'; this.body = `Redirecting to <a href="${url}">${url}</a>.`; return; } // text this.type = 'text/plain; charset=utf-8'; this.body = `Redirecting to ${url}.`; }
结合官网看更好
以上,
相关推荐
lert0 2020-08-16
瓜牛呱呱 2020-11-12
柳木木的IT 2020-11-04
yifouhu 2020-11-02
lei0 2020-11-02
源码zanqunet 2020-10-28
源码zanqunet 2020-10-26
一叶梧桐 2020-10-14
码代码的陈同学 2020-10-14
lukezhong 2020-10-14
lzzyok 2020-10-10
anchongnanzi 2020-09-21
clh0 2020-09-18
changcongying 2020-09-17
星辰大海的路上 2020-09-13
abfdada 2020-08-26
mzy000 2020-08-24
shenlanse 2020-08-18
zhujiangtaotaise 2020-08-18