阅读好的框架的源码好处多多,站在大神的角度理解整个框架的设计思路。大到架构设计,小到想要的命名风格,还有设计模式、数据结构和用于实现某些功能的算法等。在使用koa阅读某个框架的源码时,首先要使用这个框架,因为我们会知道如何使用某个API,哪里有陷阱,哪里设计精巧。我们简单的使用koa框架,如下代码constKoa=require('koa')constapp=newKoa()app.use(async(ctx,next)=>{ctx.body='HelloWorld'})app.listen(4002)运行结果w??这个服务会涉及request到response的返回数据,就这几行代码??是的,你没看错,就这几行代码就可以搭建一个服务器。下面就让我们一起来看看吧。阅读源码,进入node_modules文件夹,找到koa模块。首先看几眼README.md文件,里面介绍了koa的一些安装、使用、插件等。这里我们先跳过,然后进入package.json如下图“main”:package.json中的“lib/application.js”是对的,这是我们的入口,在lib文件夹下,我们可以看到有application.js、context.js、request.js和响应。js。经过我的修改和简化,application.js只有68行代码。可以说读起来非常简单。如下图所示:第一步是我们引入各种主要的依赖//我引入了很多我只选择阅读主框架的代码模块constresponse=require('./response');//处理响应对象constcompose=require('koa-compose');//合并和处理中间件函数constcontext=require('./context');//集成和处理上下文对象constrequest=require('./request');//集成和处理请求对象consthttp=require('http');//上面node的httpnative模块是我们主要的依赖。在Application对象中,有一个构造函数。这里主要是初始化Application对象,生成context对象,request对象,response对象。module.exports=classApplicationextendsEmitter{//初始化应用程序constructor(){super();//继承Emitterthis.middleware=[];//将中间件初始化为空数组this.context=Object.create(context);//生成上下文对象this.request=Object.create(request);//生成请求对象this.response=Object.create(response);//生成一个response对象}}看源码,不扣细节,比如说Object.create(context)生成的是什么对象?this.request对象下面是什么???,我们现在主要知道的是this.context是一个可以获取或设置请求和响应信息的对象。this.request是请求的对象,可以设置或获取请求信息的对象,this.response是响应请求对象,可以设置或获取响应参数和值的对象。大概先了解一下就够了。继续阅读。使用上述时,使用app.use(fn)和app.listen(4002)。来看看吧,源码访谈module.exports=classApplicationextendsEmitter{//InitializeApplicationconstructor(){..}listen(...args){constserver=http.createServer(this.callback());返回server.listen(...args);}使用(fn){this.middleware.push(fn);归还这个;}上面的代码非常简单。use函数是将传入的fn压入this.middleware的数组,然后返回this,方便链式调用。然后在listen中用node的原生http模块创建一个服务器。顺便说一句,使用原生http创建服务如下所示:consthttp=http.createServer((req,res)=>{res.writeHead(200,{'Content-Type':'text/plain'});res.end('okay');});http.listen(8000)继续看代码。创建服务时,在参数中调用一个this。callback()函数,让我们看看这个函数长什么样。module.exports=classApplicationextendsEmitter{//初始化Applicationconstructor(){...}listen(...args){...}use(fn){...}callback(){constfn=compose(这个.中间件);//专注于中间件数组consthandleRequest=(req,res)=>{constctx=this.createContext(req,res);//整合req,res,context,request,responsereturnthis.handleRequest(ctx,fn);//返回句柄请求};返回句柄请求;}handleRequest(ctx,fnMiddleware){consthandleResponse=()=>respond(ctx);//最终响应函数returnfnMiddleware(ctx).then(handleResponse)//处理完中间件后,传递给下一个响应函数}//创建并集成一个新的上下文。createContext(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=响应。请求=请求;上下文.res=request.res=response.res=res;request.ctx=response.ctx=上下文;请求.响应=响应;response.request=请求;context.originalUrl=request.originalUrl=req.url;context.cookies=newCookies(req,res,{keys:this.keys,secure:request.secure});request.ip=request.ips[0]||req.socket.remoteAddress||'';context.accept=request.accept=接受(请求);context.state={};返回上下文;}};上面我们可以看到回调函数中有一个constfn=compose(this.middleware);这个函数是传入this.middleware数组,然后重点处理中间件,然后返回处理完中间件的fn继续下一行consthandleRequest=(req,res)=>{constctx=this.createContext(req,res);returnthis.handleRequest(ctx,fn);};继续在handleRequest函数中输入constctx=this.createContext(req,res);this把原来的HTTP请求对象req和响应对象res作为参数传入,然后在createContext函数中(看上面最大的一个陀代码)在里面,this.request,this.response,this.context,请求对象req,和responseobjectres全部调整,经过各种整合和处理,得到一个新的context对象并返回。那就是强大的ctx。得到ctx后,下一行返回returnthis.handleRequest(ctx,fn);this.handleRequest(ctx,fn)代码如下handleRequest(ctx,fnMiddleware){consthandleResponse=()=>respond(ctx);返回fnMiddleware(ctx)。然后(处理响应)。看看这个函数我简化后的样子,如下//一些容错判断或者提示我删除所有让body=ctx.body;res.end(body);}通过ctx获取响应对象和响应值,通过end方法通知服务端所有响应头和响应主体已经发送完毕,即服务端认为完成。看上面的原生http服务方法。最后附上流程图。这里只是介绍申请的全过程,还有很多细节没有一一介绍。例如,创建上下文、请求和响应对象看起来如何?中间件是如何集中精力逐层深度处理然后返回的呢?这些细节将在下一篇文章中讨论(公司最近很忙,不知道会不会是猴年)。写得不好的地方让大家笑了。最后安利博客,喜欢的小哥哥小姐姐可以star哟网站:https://github.com/naihe138/naice-blog
