博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
koa框架会用也会写—(koa的实现)
阅读量:4086 次
发布时间:2019-05-25

本文共 7998 字,大约阅读时间需要 26 分钟。

koa框架

现在很多项目都是基于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复制代码

洋葱模型和中间件组合

洋葱模型

 

洋葱模型1

 

洋葱模型2

 

 

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的组成部分

 

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.jsconst 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.jslet 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.jslet 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.jslet 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的getdefineGetter('request','path'); //ctx代理了ctx.request.path的getdefineGetter('response','body'); //ctx代理了ctx.response.body的getdefineSetter('response','body'); //ctx代理了ctx.response.body的setmodule.exports = proto;复制代码

处理异步和错误

上面的功能都是基于同步函数,但是在node中大多数都是异步函数,所以这里面中间件的处理函数需要兼容异步函数。因为async+awit等于generator+co(koa1.0),而co中实现generator自动化是基于Promise实现的,所以这里必须函数promise化。如果不了解Promise、generator、async可以看看另一篇文章

//application.jsconst 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复制代码

结语

koa的原理基本就介绍完了,koa还有一个重要的部分就是中间件,很多功能都是中间件实现的,后面一起学习kao的中间件:

作者:梦想攻城狮
链接:https://juejin.im/post/5bab0c415188255c8473b123
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

你可能感兴趣的文章
算法导论 单链表
查看>>
算法导论 使用单链表实现栈
查看>>
算法导论 使用单链表实现队列
查看>>
算法导论 循环单链表
查看>>
算法导论 循环双链表
查看>>
算法导论 合并两个链表
查看>>
算法导论 O(n)时间内反转单链表
查看>>
深入理解按位异或运算符
查看>>
XOR Linked List – A Memory Efficient Doubly Linked List
查看>>
算法导论 XOR双向循环链表——内存高效链表
查看>>
3D数学 方向、方位和角位移
查看>>
3D数学 RotationMatrix
查看>>
3D数学 欧拉角编程
查看>>
3D数学 AABB(轴对齐矩形边界框)
查看>>
DirectX11 深度模板视图(depth-stencil)
查看>>
DirectX11 高级着色器语言HLSL入门
查看>>
DirectX11 Effect特效文件
查看>>
DirectX11 多重纹理
查看>>
DirectX11 光照
查看>>
图形学 图形渲染管线
查看>>