查看Koa,version@2.7源码,只有application.js、context.js、request.js、response.js四个文件;分别对应Koa应用入口、上下文环境、请求对象和响应对象,application.js1,overview可以看到这个模块使用了Node内部模块events作为父类,并导出了一个用于创建Koa应用的构造函数。这个构造函数中有几个属性值。我们主要看2.属性中间件,保存中间件数组;上下文,上下文对象;请求,请求对象;响应,响应对象;3、方法3.1listen我们一般在使用的时候只传入一个数字,让服务在某个端口号上启动。可以看到listen内部调用的是node创建httpserver的方法。这里我们主要看一下接受请求的http.createServer方法的监听函数。同时这个函数的两个参数分别是req请求对象和res响应对象;查看回调方法,callback(){constfn=compose(this.middleware);如果(!this.listenerCount('error'))this.on('error',this.onerror);consthandleRequest=(req,res)=>{constctx=this.createContext(req,res);返回这个.handleRequest(ctx,fn);};返回句柄请求;}开始通过compose返回链式处理中间件的功能,这里是对compose方法的封装。先检查中间件数组是否满足要求,然后在返回的函数中可以看到中间件的执行结果会通过promise.resolve返回。中间件的第一个参数是ctx,第二个参数是next。这里可以看到dispatch对应的是next,当我们执行这个方法的时候,中间件会继续递归调用。这就形成了对Koa中洋葱模型执行顺序的承诺。//componse方法functioncompose(middleware){if(!Array.isArray(middleware))thrownewTypeError('Middlewarestackmustbeanarray!')for(constfnofmiddleware){if(typeoffn!=='function')thrownewTypeError('中间件必须由函数组成!')}/***@param{Object}context*@return{Promise}*@apipublic*/returnfunction(context,next){//最后调用的中间件#letindex=-1returndispatch(0)functiondispatch(i){if(i<=index)returnPromise.reject(newError('next()被多次调用'))index=iletfn=middleware[i]if(i===middleware.length)fn=nextif(!fn)returnPromise.resolve()try{returnPromise.resolve(fn(context,dispatch.bind(null,i+1)));}catch(err){returnPromise.reject(err)}}}}然后看handleRequest,这里有点懵。他在回调中定义了一个函数叫handleRequest,不过后者只是一个函数的变量名,里面的handleRequest就是我们要分析的。为了方便解释和查看,可以暂时改写如下。callback(){constfn=compose(this.middleware);如果(!this.listenerCount('error'))this.on('error',this.onerror);constxxx=(req,res)=>{constctx=this.createContext(req,res);返回this.handleRequest(ctx,fn);};返回xxx;}在xxx函数中,首先使用createContext方法将请求消息、响应消息等对象挂载到context对象上,request为Koa请求对象,response为Koa响应对象,app为当前Koa服务实例对象,req是Node请求对象,res是Node响应对象。然后执行handleRequest方法,参数是上下文和处理中间件的函数。返回上次请求的执行结果。这里可以看到返回状态码默认为404createContext(req,res){constcontext=Object.create(this.context);constrequest=context.request=Object.create(this.request);constresponse=context.response=Object.create(this.response);context.app=request.app=response.app=this;context.req=request.req=response.req=req;context.res=request.res=response.res=res;request.ctx=response.ctx=上下文;请求.响应=响应;response.request=请求;context.originalUrl=request.originalUrl=req.url;context.state={};返回上下文;}handleRequest(ctx,fnMiddleware){constres=ctx.res;res.statusCode=404;constonerror=err=>ctx.onerror(err);consthandleResponse=()=>respond(ctx);onFinished(res,onerror);returnfnMiddleware(ctx).then(handleResponse).catch(onerror);}3.2handleRequest,createContext上面回调中用到的两个方法3.3useuse方法很简单,就是把单个中间件push成数组保存3.3toJSONtoJSON方法引入了一个onlyPackage,打开可以看到它过滤了一个对象,返回了一个新的只包含指定属性的对象Value对象3.4onerror打印错误日志3.5respond处理响应结果对象,涉及buffer和stream的一些概念,最后返回响应结果2.context.js返回context环境的对象原型1.inspect返回当前对象2.toJSON返回请求消息和响应消息的对象以及一些其他属性3、assert、onerror异常处理4、获取cookie、设置cookiecookie值和赋值函数5、delegate从上面看到当前上下文原型的属性和方法,通过delegate定义其他属性的主要方式是查看delegate依赖包this.proto=proto;this.target=目标;这个.methods=[];this.getters=[];this.setters=[];this.fluents=[];}返回一个以Delegator为构造函数的对象,返回的对象定义了上述属性proto参数为传入的context原型对象,target是因为调用了两次Delegator函数,分别是response和request,以及Delegator原型上有method,access,getter,setter,fluent几个方法,调用后返回的是原始对象,所以在context.js中使用时可以链式调用。以method方法为例:Delegator.prototype.method=function(name){varproto=this.proto;vartarget=this.target;这个.methods.push(名字);proto[name]=function(){returnthis[target][name].apply(this[target],arguments);};返回这个;};调用这个方法,先将方法名保存到方法集合数组中,然后在原对象上定义属性方法,此时proto为上下文原型对象,然后传递target参数表示方法是否使用请求对象上的响应或方法。总结就是上下文原型对象定义了response、request等重要属性。同时,这两个对象中的方法快速直接绑定到原型对象上,所以一些属性或方法可以在没有响应和请求的情况下使用。3、request.js定义一个请求对象,在该对象上挂载Node请求消息req的一些属性和方法并返回1,header,headers都表示请求头2,url等3,也就是一些Node中的request对象中的属性挂载在Koa请求对象request上4.response.js类似于request,返回一个response对象,然后查看application.js中的createContext方法;其中request为Koa请求对象,response为Koa响应对象,app为当前Koa服务实例对象,req为Node的请求对象,res为Node响应对象,其他方法包括一些响应结果返回,重定向等方法,不详述。
