内容简介:现在很多项目都是基于koa框架实现的,主要是因为koa小巧轻便,采用插件式扩展,可以根据需要的功能来选用不同的插件,开发起来更加的方便快捷。所以了解koa的实现原理是十分、十分、十分有必要的。koa洋葱模型的实现,其实就是通过use将函数存放在一个middlewares队列中,然后通过函数dispatch派发中间件。koa主要是由四部分组成:
现在很多项目都是基于koa框架实现的,主要是因为koa小巧轻便,采用插件式扩展,可以根据需要的功能来选用不同的插件,开发起来更加的方便快捷。所以了解koa的实现原理是十分、十分、十分有必要的。
koa的使用分析
const Koa = require('koa');
let app = new Koa();//Koa是一个类,通过new生成一个实例
//koa的原型上有use方法,来注册中间件
app.use((ctx,next)=>{
//koa拥有ctx属性,上面挂载了很多属性
console.log(ctx.req.path);
console.log(ctx.request.req.path);
console.log(ctx.request.path);
console.log(ctx.path);
next();//洋葱模型,中间件组合
})
app.listen(3000);//Koa的原型上拥有监听listen
复制代码
洋葱模型和中间件组合
洋葱模型
const Koa = require('koa');
const app = new Koa();
app.use(async (ctx, next)=>{
console.log(1)
await next();
console.log(2)
});
app.use(async (ctx, next) => {
console.log(3)
await next();
console.log(4)
})
app.use(async (ctx, next) => {
console.log(5)
awit next();
console.log(6)
})
//打印结果:1 3 5 6 4 2
复制代码
中间件组合
koa洋葱模型的实现,其实就是通过use将函数存放在一个middlewares队列中,然后通过函数dispatch派发中间件。
- dispatch组合中间件:
let app = {
middlewares:[]; //缓存队列
use(fn){ //注册中间件
this.middlewares.push(fn);
}
}
app.use(next=>{
console.log(1)
next();
console.log(2)
});
app.use(next => {
console.log(3)
next();
console.log(4)
})
app.use(next => {
console.log(5)
next();
console.log(6)
})
dispatch(0)
function dispatch(index){ //派发执行中间件
if(index===app.middlewares.length) retrun ;
let middleware = app.middlewares[index];
middleware(()=>{
dispatch(index+1);
})
}
复制代码
- Array.prototype.reduceRight组合中间件:
let app = {
middlewares:[];//缓存队列
use(fn){//注册中间件
this.middlewares.push(fn);
}
}
app.use(next=>{ //fn1(next) next => fn2
console.log(1)
next();
console.log(2)
});
app.use(next => { //fn2(next) next => fn3
console.log(3)
next();
console.log(4)
})
app.use(next => { //fn3(next) next => null;
console.log(5)
next();
console.log(6)
})
let fn= compose(app.middlewares)
function conpose(middle){
return middles.reduceRight((a,b)=>{ //收敛成一个函数
return function(){
b(a);
}
},()=>{});
}
fn();
//fn3(next) next:() => {};
//fn2(next) next:() => fn3(()=>{})
//fn1(next) next:() => fn2(()=>fn3(()=>{}))
复制代码
- Array.prototype.reduce组合中间件:
let app = {
middlewares:[];//缓存队列
use(fn){//注册中间件
this.middlewares.push(fn);
}
}
app.use(next=>{ //fn1(next) next => fn2
console.log(1)
next();
console.log(2)
});
app.use(next => { //fn2(next) next => fn3
console.log(3)
next();
console.log(4)
})
app.use(next => { //fn3(next) next => null;
console.log(5)
next();
console.log(6)
})
let fn= compose(app.middlewares)
function conpose(middle){
return middles.reduce((a,b)=>{ //收敛成一个函数
return (arg)=>{
a(()=>{b(arg)})
}
});
}
fn(()=>{});
复制代码
koa的组成部分
koa主要是由四部分组成:
- application:koa的主要逻辑,包含了中间件处理过程
- context:koa关于ctx的封装
- request:koa请求对象的封装
- response:koa响应对象封装
koa的实现
Koa类初始化
- Koa是一个类,拥有middleware、ctx、request、response
- Koa.prototype拥有use注册中间件
- Koa.prototype拥有listen监听网络请求,其内部是对http模块的封装
- Koa中handleRquest处理上下文ctx和中间件middleware
//application.js
const http = require('http');
let context = require('./context');
let request = require('./request');
let response = require('./response');
class Koa {
constructor(){
this.middlewares = [];
// 原型继承,防止引用空间的问题使后加的属性也会加在这个对象上
//this.context和引入的context不是同一个对象
this.context = Object.create(context);
this.request = Object.create(request);
this.response = Object.create(response);
}
use(fn){
this.middlewares.push(fn) ;
}
//挂载封装处理ctx
createContext(req,res){
let ctx = this.context;
ctx.request = this.request;
ctx.response = this.response;
ctx.req=ctx.request.req =req;
ctx.res=ctx.response.res=res;
return ctx;
}
//组合中间件
compose(ctx,middles){
function dispatch(index){
if(index === middle.length) return;
let middle = middles[index];
middle(ctx,()=>dispatch(index+1));
}
dispatch(0);
}
//网络请求监听回调
handleRequest(req,res){
let ctx = createContext(req,res);
this.compose(ctx,this.middlewares);
}
listen(...args){
let server = http.createServer(this.handleRquest);
server.listen(...args)
}
}
module.exports = Koa
复制代码
request封装
request上扩展url、path等属性
//request.js
let request = {
//类似Object.defineProperty(request,'url'){get(){}}
get url(){
//this.req => ctx.request.req = req,调用时ctx.request.url
this.req.url;
}
get path(){
let url = require('url');
return url.parse(this.req.url).pathname;
}
}
module.exports = request;
复制代码
response封装
request上扩展body等属性
//response.js
let response = {
get body(){
return this._body;
}
set body(val){ //设置内置的_body来存储
this._body=val
}
}
module.exports = response;
复制代码
ctx封装
ctx属性代理了一些ctx.request、ctx.response上的属性,使得ctx.xx能够访问ctx.request.xx或ctx.response.xx
//context.js
let proto = {
};
function defineGetter(property,key){
proto.__defineGetter(key,function(){
return this[property][key];
})
}
function defineSetter(property,key){
proto.__defineSetter(key,function(val){
this[property][key] = val;
})
}
defineGetter('request','url'); //ctx代理了ctx.request.url的get
defineGetter('request','path'); //ctx代理了ctx.request.path的get
defineGetter('response','body'); //ctx代理了ctx.response.body的get
defineSetter('response','body'); //ctx代理了ctx.response.body的set
module.exports = proto;
复制代码
处理异步和错误
上面的功能都是基于同步函数,但是在node中大多数都是异步函数,所以这里面中间件的处理函数需要兼容异步函数。因为async+awit等于generator+co(koa1.0),而co中实现generator自动化是基于Promise实现的,所以这里必须函数promise化。如果不了解Promise、generator、async可以看看另一篇文章 promise原理就是这么简单
//application.js
const http = require('http');
let context = require('./context');
let request = require('./request');
let response = require('./response');
let Stream = require('stream');
let EventEmitter = require('events');
class Koa extends EventEmitter { //继承EE,处理错误
constructor(){
this.middlewares = [];
this.context = Object.create(context);
this.request = Object.create(request);
this.response = Object.create(response);
}
use(fn){
this.middlewares.push(fn) ;
}
createContext(req,res){
let ctx = this.context;
ctx.request = this.request;
ctx.response = this.response;
ctx.req=ctx.request.req =req;
ctx.res=ctx.response.res=res;
return ctx;
}
compose(ctx,middles){
function dispatch(index){
//没有注册中间件,返回一个promise
if(index === middle.length) return Promise.resolve();
let middle = middles[index];
// Promise化,next一定为promise
return Promise.resolve(middle(ctx,()=>dispatch(index+1)));
}
return dispatch(0);
}
handleRequest(req,res){
res.statusCode = 404;
let ctx = createContext(req,res);
//所有的中间件执行时候,可以执行内置逻辑,处理错误等
let p = this.compose(ctx,this.middlewares);
p.then(()=>{
//统一处理res.body的不同情况
let body = ctx.body;
if (Buffer.isBuffer(body) || typeof body === 'string'){
res.setHeader('Content-Type','text/plain;charset=utf8')
res.end(body);
} else if (body instanceof Stream){
body.pipe(res);
}else if(typeof body == 'object'){
res.setHeader('Content-Type','application/json;charset=utf8')
res.end(JSON.stringify(body));
}else{
res.end('Not Found');
}
}).catch(e=>{ //处理错误
this.emit('error',e);
res.statusCode = 500;
//_http_server可以根据状态码找到对应的类型字段
res.end(require('_http_server').STATUS_CODES[res.statusCode]);
})
}
listen(...args){
let server = http.createServer(this.handleRquest);
server.listen(...args)
}
}
module.exports = Koa
复制代码
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 自己实现集合框架(十):顺序栈的实现
- Golang实现简单爬虫框架(4)——队列实现并发任务调度
- 简易RPC框架实现
- 优秀开源框架的扩展机制实现
- Go 实现简易 RPC 框架
- 如何实现一个Python爬虫框架
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Is Parallel Programming Hard, And, If So, What Can You Do About
Paul E. McKenney
The purpose of this book is to help you understand how to program shared-memory parallel machines without risking your sanity.1 By describing the algorithms and designs that have worked well in the pa......一起来看看 《Is Parallel Programming Hard, And, If So, What Can You Do About 》 这本书的介绍吧!