如何从头开始写Koa2框架
01.介绍
基于Node.js平台的下一代web开发框架Koa是一个全新的web框架,由Express背后的原团队打造,致力于成为web应用和API开发领域更小、更具表现力、更健壮的基石。与对应的Express相比,Koa更小,更健壮。本文将带你从零开始实现Koa的源代码,从根源上解决你对Koa的困惑。Koa版本是2.7.0,如果版本不同,源代码可能会有变化。02.源代码目录介绍。
Koa源目录截图
根据来源目录,Koa主要分为四个部分,即:
application: Koa的主要模块,对应app应用对象context:对应ctx对象request:对应Koa中的request对象response:是Koa的全部内容,应用是核心文件。我们将从这个文档开始,逐步实现Koa框架
03.实现基本的服务器代码目录
我的申请
const { CreateServer }=require(' http ');模块。exports=class application { constructor(){//初始化中间件数组,所有中间件函数将被添加到当前数组中。this . middleware=[];}//使用中间件方法use(fn) {//将所有中间件功能添加到中间件数组this . middleware . push(fn);}//监听端口号方法监听(.args) {//使用nodejs的http模块监听端口号const server=createserver ((req,RES)={/*处理请求的回调函数。这里所有的中间件函数req都是节点的原始请求对象res,节点的原始响应对象RES */this . middleware . foreach((fn)=fn(req,RES));}) server.listen(.args);}}index.js
//引入自定义模块const mykoa=require('。/js/my-application’);//创建实例对象const app=new MyKoa();//使用中间件app.use((req,res)={console.log('执行的中间件功能~ ~ ~ 111 ');}) app.use ((req,res)={console.log('执行的中间件功能~ ~ ~ 222 ');RES . end(' hello MyKoa ');})//监听端口号app.listen(3000,err={if(!Err) console.log('服务器已成功启动');else console . log(err);})运行入口文件index.js后,通过浏览器输入URL,访问http://localhost33603000/,即可看到结果~ ~
太神奇了!构建了最简单的服务器模型。当然,我们的极简服务器还有很多问题。让我们一个一个地解决它们
04.实现中间件功能的下一个方法
提取createServer的回调函数,并将其打包成回调方法(可重用)
//监听端口号方法监听(.args) {//监听端口号const server=createserver(这。回调())使用nodejs的http模块;server.listen(.args);}回调(){ const handleRequest=(req,RES)={ this . middleware . foreach((fn)=fn(req,RES));}返回handleRequest}封装复合函数以实现下一个方法
//函数Compose(中间件),负责执行中间件函数{//Compose方法的返回值是一个函数,这个函数的返回值是一个promise对象。//当前函数是调度return (req,res)={//默认调用一次,以便执行第一个中间件函数return dispatch(0);函数dispatch(i) {//提取中间件数组fn的函数让fn=中间件[I];//如果最后一个中间件也调用下一个方法,它将直接返回一个promise对象if(!fn)返回Promise . resolve();/* dispatch.bind(null,i 1))作为中间件函数调用的第三个参数,实际上是next对应的一个栗子:如果i=0,那么dispatch.bind(null,1))-也就是说,如果调用下一个方法,它实际上执行dispatch(1) -它使用递归重新进入并取出下一个中间件函数,然后执行fn (req,RES,dispatch)。bind (null,i1))-这就是中间件函数可以有三个参数的原因。调用时,我们传入了*/returnpromise.resolve (fn (req,res,dispatch.bind (null,i1));}}}使用撰写功能
回调(){//执行合成方法返回一个函数constfn=compose(这。中间件);Const handlerequest=(req,res)={//调用此函数,返回值为promise对象//然后方法被触发,表示所有中间件函数都已被调用完成fn(req,RES)。然后(()={//这里是所有处理函数的最后一级,允许返回response ~ });}返回handleRequest}修改条目文件index.js代码
//引入自定义模块const mykoa=require('。/js/my-application’);//创建实例对象const app=new MyKoa();//使用中间件app.use ((req,RES,next)={console.log('执行的中间件功能~ ~ ~ 111 ');//调用下一个方法意味着调用栈中下一个中间件函数next();}) app.use ((req,RES,next)={console.log('执行的中间件功能~ ~ ~ 222 ');RES . end(' hello MyKoa ');//最后一个next方法不调用下一个中间件函数,而是直接返回promise . resolve()next();})//监听端口号app.listen(3000,err={if(!Err) console.log('服务器已成功启动');else console . log(err);})这时我们实现了下一个方法,核心是compose函数,极简代码实现了这个函数,太不可思议了!
05.处理返回一个响应
定义返回响应函数response
函数response(req,res) {//获取set body数据让body=res.bodyIf(type of body==' object '){//如果是对象,转换成json数据,返回body=JSON。瘦长的(身体);res.end(正文);} else {//默认情况下,其他数据直接返回RES . end(body);}}在回调中被调用
回调(){ const fn=compose(this . middleware);Const handlerequest=(req,RES)={//当所有中间件功能都执行完了,那么就会触发方法,然后执行方法返回响应const handle response=()=response(req,RES);fn(请求,资源)。然后(handleResponse);}返回handleRequest}修改条目文件index.js代码
//引入自定义模块const mykoa=require('。/js/my-application’);//创建实例对象const app=new MyKoa();//使用中间件app.use ((req,RES,next)={console.log('执行的中间件功能~ ~ ~ 111 ');next();}) app.use ((req,RES,next)={console.log('执行的中间件功能~ ~ ~ 222 ');//设置响应内容,框架负责返回响应~ res.body=' hello myKoa})//监听端口号app.listen(3000,err={if(!Err) console.log('服务器已成功启动');else console . log(err);})这时我们可以处理不同的回复内容~当然还是比较简单的,然后我们可以展开~
06.定义请求模块
//此模块需要npm下载const parse=require(' parse URL ');const QS=require(' query string ');module.exports={/** *获取请求头信息*/getheaders(){返回this . req . headers;},/* * *设置请求头信息*/setheaders (val) {this。请求。headers=val},/* * *获取查询字符串*/获取查询(){//解析查询字符串参数-key 1=value 1 key 2=value 2 const query string=parse(this。请求)。查询;//将其解析为对象并返回-{key1:value1,key 2: value 2 } return QS . parse(query string);}}07.定义响应模块
module.exports={/** *设置响应头的信息*/set (key,value) {this.res.setheader (key,value);},/* * *获取响应状态代码*/getstatus(){ return this . RES . status code;},/* * *设置响应状态代码*/设置状态(代码){ this . RES . status code=code;},/* * *获取响应正文信息*/获取正文(){返回此。_ body},/* * *设置响应正文信息*/设置正文(val) {//设置响应正文内容本。_ body=val//设置响应状态代码this.status=200//JSON if(Type of val==' object '){ this . set(' Content-Type ',' application/JSON ');} },}08.定义上下文模块
//此模块需要新公共管理下载const delegate=require(' delegates ');const proto=模块。导出={ };//将反应对象上的属性/方法克隆到样机上委托(原型,"响应")。方法(“set”)//克隆普通方法。访问('状态')//克隆带有得到和设置描述符的方法。访问('正文')//将请求对象上的属性/方法克隆到样机上委托(原型,"请求")。访问('查询')。getter(' header ')//克隆带有得到描述符的方法09、揭秘代表模块
module.exports=Delegator/** *初始化一个委托人。*/函数委托者(原型,目标){ //这个必须指向委托人的实例对象if(!(委托人的这个实例))返回新的委托人(原型,目标);//需要克隆的对象this.proto=proto//被克隆的目标对象this.target=target//所有普通方法的数组这个。methods=[];//所有带有得到描述符的方法数组这个。getter=[];//所有带有设置描述符的方法数组这个。setters=[];}/** * 克隆普通方法*/delegator。原型。method=function(name){//需要克隆的对象this.proto//被克隆的目标对象var target=this.target//方法添加到方法数组中this.methods.push(名称);//给样机添加克隆的属性proto[name]=function(){ /* this指向原型,也就是中强举个栗子:CTX。回应。准备好。申请(CTX。回应、论据)参数对应实参列表,刚好与应用方法传参一致执行' ctx.set('键','值)实际上相当于执行response.set('key ',' value') */返回这个[目标][名称].应用(此[目标],参数);};//方便链式调用归还这个;};/** * 克隆带有得到和设置描述符的方法*/委托人。原型。access=function(name){返回这个。getter(名称).setter(名称);};/** * 克隆带有得到描述符的方法*/委托人。原型。getter=function(name){ var proto=this。protovar target=this.targetthis。吸气器。推送(名称);//方法可以为一个已经存在的对象设置得到描述符属性原型__defineGetter__(名称,函数(){)返回这个[目标][名称];});归还这个;};/** * 克隆带有设置描述符的方法*/委托人。原型。setter=function(name){ var proto=this。protovar target=this . targetthis . setters . push(名称);//方法可以为一个已经存在的对象设置设置描述符属性原型__defineSetter__(name,function(val){ return this[target][name]=val;});归还这个;};10、使用中强取代请求和表示留数
修改我的申请
const { CreateServer }=require(' http ');const context=require(' ./my-context ');const request=require(' ./my-request ');const response=require(' ./my-response’);模块。导出=类Application { constructor(){ this。中间件=[];//对象。创建(目标)以目标对象为原型,创建新对象,新对象原型有目标对象的属性和方法这.上下文=对象.创建(上下文);这个. request=Object.create(请求);this.response=Object.create(响应);}使用{这个。中间件。push(fn);}听(.args) { //使用开发的超文本传送协议(超文本传输协议的缩写)模块监听端口号const server=CreateServer(这。callback());server.listen(.args);}回调(){ const fn=compose(this。中间件);const handleRequest=(req,res)={ //创建上下文常量CTX=这个。创建上下文(请求、资源);const handleResponse=()=response(CTX);fn(ctx).然后(handleResponse);}返回handleRequest} //创建语境上下文对象的方法createContext(req,res) { /*凡是请求/决议,就是结节原生对象凡是请求/响应,就是自定义对象这是实现互相挂载引用,从而在任意对象上都能获取其他对象的方法*/const context=对象。创建(这个。上下文);const request=上下文。请求=对象。创建(这个。请求);常量响应=上下文。响应=对象。创建(这个。回应);语境。app=请求。app=响应。app=this语境。req=请求。req=响应。req=req语境。RES=请求。RES=响应。RES=RES请求。CTX=回应。CTX=背景;request . response=response . request=request返回上下文;}}//将原来使用请求,资源的地方改用ctxfunction撰写(中间件){退货(CTX)={退货派单(0);函数分派(i) {让fn=中间件[一];if(!fn)返回答应我。resolve();返回Promise.resolve(fn(ctx,dispatch.bind(null,I 1)));} } }功能反应(CTX){让身体=CTX。身体;const RES=CTX . resif(body的类型==' object '){ body=JSON。stringify(身体);res.end(正文);} else { RES . end(正文);}}修改入口文件index.js代码
//引入自定义模块const MyKoa=require(' ./js/my-application’);//创建实例对象const app=new MyKoa();//使用中间件app.use((ctx,next)={ console.log('中间件函数执行了~~~111');next();})app.use((ctx,next)={ console.log('中间件函数执行了~~~222');//获取请求头参数控制台。原木(CTX。标题);//获取查询字符串参数控制台。原木(CTX。查询);//设置响应头信息ctx.set('content-type ',' text/html;charset=utf-8 ');//设置响应内容,由框架负责返回响应~ CTX。body=' h1 hello myKoa/h1 ';})//监听端口号app.listen(3000,err={ if(!' err) console.log('服务器启动成功了');其他控制台。日志(err);})到这里已经写完了寇阿相思树主要代码,有一句古话-看万遍代码不如写上一遍。还等什么,赶紧写上一遍吧~当你能够写出来,再去阅读源码,你会发现源码如此简单~以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。
版权声明:如何从头开始写Koa2框架是由宝哥软件园云端程序自动收集整理而来。如果本文侵犯了你的权益,请联系本站底部QQ或者邮箱删除。

















