内容简介:现在很多项目都是基于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爬虫框架
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
编程珠玑(英文版・第2版)
[美] Jon Bentley / 人民邮电出版社 / 2010-8 / 39.00元
多年以来,当程序员们推选出最心爱的计算机图书时,《编程珠玑》总是位列前列。正如自然界里珍珠出自细沙对牡蛎的磨砺,计算机科学大师Jon Bentley以其独有的洞察力和创造力,从磨砺程序员的实际问题中凝结出一篇篇不朽的编程“珠玑”。这些文章是《ACM通讯》最受欢迎的专栏文章,最终结集为两部书出版。本书为第一卷,主要讨论计算机科学中最本质的问题:如何正确选择和高效地实现算法。 在书中,作者选取许......一起来看看 《编程珠玑(英文版・第2版)》 这本书的介绍吧!