Koa的源码大致分为四个文件
application.js、context.js、request.js、response.js.下面是每个文件的源码实现加注释。
- context.js
context文件主要是实现获取属性的代理。例如:ctx.path -> ctx.request.path的映射。
let context = {};
// 如果去context 上去值 我希望去context.request上取
// js有一个不常用的方法 Object.defineProperty变种
function defineGetter(key,property) { // 定义获取器
context.__defineGetter__(property, function () {
return this[key][property];
})
}
// 给某个属性定义setter
function defineSetter(key,property) {
context.__defineSetter__(property, function (value) {
this[key][property] = value
})
}
// 代理 把取属性的值 通过request来获取
defineGetter('request','path');
defineGetter('request','url');
defineGetter('request','query');
// ctx.body => ctx.response.body;
defineGetter('response','body');
// ctx.body = '100' ctx.response.body = '100'
defineSetter('response','body');
module.exports = context
- request.js
处理请求的一些属性,例如:ctx.request.url -> ctx.request.req.url
let url = require('url')
let request = {
get url(){
return this.req.url;
},
get path(){
let { pathname } = url.parse(this.req.url);
return pathname
},
get query() {
let { query } = url.parse(this.req.url,true);
return query
}
};
module.exports = request;
- response.js
处理响应的一些属性。例如body
let response = { // 主要是封装关于响应的内容
set body(value){
this.res.statusCode = 200; // 表明成功了
this._body = value;
},
get body(){
return this._body
}
};
module.exports = response;
- application.js
let http = require('http');
let path = require('path');
let fs = require('fs');
let context = require('./context');
let request = require('./request');
let response = require('./response');
let EventEmitter = require('events');
let Stream = require('stream');
class Koa extends EventEmitter{
constructor() {
super();
this.middleware; // Object.create(null); 没有原型的对象
this.context = Object.create(context);
this.request = Object.create(request);
this.response = Object.create(response);
this.middlewares = [];
}
// 注册中间件的方法
use(fn) {
this.middlewares.push(fn);
}
// 创建上下文
createContext(req, res) { // 自己封装request 和response属性
let ctx = this.context;
ctx.request = this.request;
ctx.req = ctx.request.req = req; // 请求相关的
ctx.response = this.response;
ctx.res = ctx.response.res = res; // 响应相关的
return ctx;
}
// 组合方法
compose(middles, ctx) {
function dispatch(index) {
// 如果没有注册中间件 需要返回一个成功的promise
if(index === middles.length) return Promise.resolve();
let middle = middles[index];
// 让第一个函数执行完 如果有异步要看一看 有没有await, 必须要返回一个promise
return Promise.resolve(middle(ctx, () => dispatch(index + 1)));
}
return dispatch(0);
}
// 处理用户请求到来时
handleRequest(req, res) {
let ctx = this.createContext(req, res);
res.statusCode = 404; //默认我就认为你返回的404没有调用ctx.body;
let fn = this.compose(this.middlewares, ctx);
// 把所有的函数进行组合 当组合的函数执行成功后 把最终的结果进行响应
fn.then(()=>{
if (!ctx.body){
res.end('Not Found')
} else if (ctx.body instanceof Stream){
res.setHeader('Content-Type','text/html;charset=utf-8')
ctx.body.pipe(res);
}else if (typeof ctx.body ==='object'){
res.setHeader('Content-Type','application/json;charset=utf-8');
res.end(JSON.stringify(ctx.body));
}else{
res.end(ctx.body); // 用res.end结束
}
}).catch(err=>{
this.emit('error',err);
})
}
listen(...args) {
// 启动服务
let server = http.createServer(this.handleRequest.bind(this));
server.listen(...args);
}
}
module.exports = Koa;