koa原理
koa next()的原理
let app = {};
app.middlewares = [];
app.use = function (cb){
app.middlewares.push(cb)
}
app.use((next)=>{
console.log(1)
next()
console.log(2)
})
app.use((next)=>{
console.log(3)
next()
console.log(4)
})
// 第一种
function dispatch(index){
if(index === app.middlewares.length) return ()=>{};
let route = app.middlewares[index];//第一次的中间件
route(()=>dispatch(index+1));
}
dispatch(0)
// 第二种
let fn = app.middlewares.reduceRight((a,b)=>{
return ()=>b(a)
},()=>{})
let fn = app.middlewares.reduceRight((a,b)=>()=>b(a),()=>{})
fn()
// 第三种
let fn = app.middlewares.reduce((a,b)=>{
return function(...args){
return a(()=>b(...args))
}
})
let fn = app.middlewares.reduce((a,b)=>(...args)=> a(() => b(...args)));
fn(()=>{})
- koa源码
- 4个文件
- koa/application.js
- 核心编译文件(入口)
- 里面有一个类 提供了2个方法
- use方法
- 处理next()方法
- listen方法
- 开启服务
- use方法
- createContext方法
- 将实例挂在到ctx上
- handleRequest方法
- 处理请求数据的类型 加请求头
- koa/context.js
- 上下文主要往ctx上挂载内容
- 用的getter和setter
- koa/request.js
- 将ctx.request赋值给this.req
- koa/response.js
- 将响应的实例赋值给ctx.body
- koa/application.js
let EventEmitter = require('events');
let http = require('http');
let context = require('./context');
let request = require('./request');
let response = require('./response');
let Stream = require('stream');
class Application extends EventEmitter{
constructor(){
super();
this.middlewares = [];
this.context = Object.create(context);
this.request = Object.create(request);
this.response = Object.create(response);
}
createContext(req,res){
let ctx = this.context;
ctx.request = this.request; // 是koa内部自己封装的
ctx.response = this.response;
ctx.req = ctx.request.req = req; // ctx.req res是默认的请求和响应
ctx.res = ctx.response.res = res;
return ctx;
}
// 处理当前的请求的方法
compose(ctx,middlewares){ // 处理了promise的逻辑
function dispatch(index) {
if(index === middlewares.length) return Promise.resolve();
let middleware = middlewares[index];
return Promise.resolve(middleware(ctx,()=>dispatch(index+1)))
}
return dispatch(0);
}
handleRequest(req,res){
// 先要创建一个context对象
let ctx = this.createContext(req,res);
// 要把所有的中间件进行组合
res.statusCode = 404;
// res.setHeader('Content-Disposition', 'attachment');
let p = this.compose(ctx,this.middlewares);
// 我希望等待所有中间件执行完后 在取出ctx.body把结果响应回去
p.then(function () {
let body = ctx.body;
if (body instanceof Stream) { // 先判断流,在判断是不是对象
body.pipe(res); // 异步方法
}else if(typeof(body) === 'number'){
res.setHeader('Content-Type', 'text/plain;charset=utf8');
res.end(body.toString());
}else if(typeof body == 'object'){
res.setHeader('Content-Type','application/json;charset=utf8');
res.end(JSON.stringify(body));
}else if(typeof body === 'string' || Buffer.isBuffer(body)){
res.setHeader('Content-Type', 'text/plain;charset=utf8');
res.end(body);
}else{
res.end(`Not Found`);
}
}).catch(e=>{
this.emit('error',e);
});
}
// 中间件方法 用来收集中间件的
use(callback){
this.middlewares.push(callback);
}
// 创建服务并监听端口号
listen(...args){
let server = http.createServer(this.handleRequest.bind(this));
server.listen(...args);
}
}
module.exports = Application;
- koa/context.js
let proto = {
}
function defineGetter(property,key) {
proto.__defineGetter__(key,function () {
return this[property][key];
});
}
function defineSetter(property,key) {
// ctx.body = '123' ctx.response.body = 123
proto.__defineSetter__(key,function (value) {
this[property][key] = value;
});
};
defineGetter('request','path');
defineGetter('request','url');
defineGetter('response','body');
defineSetter('response','body');
module.exports = proto;
- koa/request.js
let url = require('url')
module.exports = { // ctx.request.url => this=>ctx.request
get url(){
return this.req.url
},
get path(){
let {pathname} = url.parse(this.req.url,true);
return pathname;
}
}
- koa/response.js
module.exports = {
set body(val){
this.res.statusCode = 200; // 调用ctx.body = xxxx 需要把状态设置成200
this._body = val
},
get body(){
return this._body
}
}