在抖音上,有幸看到一位程序员讲解如何阅读源码。主要分为三个步骤:领悟思想、把握设计、体验细节。领悟思想:只需要理解作者设计框架的初衷和目的把握设计:只需要理解代码的接口和抽象类以及宏观设计经验细节:基于顶层抽象接口设计,并根据以上三步法逐步展开代码的图片,迫不及待地进行了Express手术。如果这个源码分析有什么不对的地方,读者可以在下方留言,我们一起交流。一、领悟思想Express中文网介绍了Express是基于Node.js平台的快速、开放、极简的Web开发框架。在这句话中,可以解读以下意思:Express基于Node.js平台,具有快速、极简的特点,表明其初衷是通过扩展Node.js的功能来提高开发效率。框架的开放性意味着框架不会对开发者施加过多的限制,可以自由发挥自己的想象力来扩展功能。Express是一个web开发框架,说明作者的定位是帮助我们更方便的处理HTTP请求和响应。2.把握设计在了解了作者的设计思路后,下面从源码目录、核心设计原则、抽象接口等方面对Express进行整体把握。2.1源码目录下面是Express的源码目录,比较简单。├─application.js---创建Express应用后可以直接调用的api都在这里(核心)├─express.js---入口文件,创建Express应用├─request.js---enrich├─response.js中请求实例上的http函数├─response.js---丰富http中响应实例上的函数├─utils.js---工具函数├─view.js---模板渲染相关内容├─router---路由相关的内容(核心)|├─index.js|├─layer.js|└route.js├─middleware---中间件相关的内容|├─init.js---将挂载request和response中在原有request和response原型基础上新增功能|└query.js---将requesturl中的query部分添加到request的query属性中2.2源码抽象接口对目录有一定的了解在了解系统结构之后,我们将通过UML类图进一步了解系统各模块的依赖关系,为后续的源码分析打下坚实的基础。2.3设计原则这部分是整个Express框架的核心。下图展示了整个框架的运行过程。看看它是不是很混乱。为了理解这部分,需要弄清楚四个概念:Application、Router、Layer、Route。为了理清以上四个概念,先介绍一段代码constexpress=require('./express');constres=require('./response');constapp=express();app.get('/test1',(req,res,next)=>{console.log('one');next();},(req,res)=>{console.log('two');res.end('two');})app.get('/test2',(req,res,next)=>{console.log('three');next();},(req,res)=>{console.log('four');res.end('four');})app.listen(3000);1.Application表示一个Express应用,可以通过express()创建。2、Router路由系统用于调度整个系统的运行。上述代码中,路由系统包括两部分:app.get('/test1',...)和app.get('/test2',...)3.Layer表示一层。对于上面的代码,app.get('/test1',...)和app.get('/test2',...)都可以成为一个Layer4.Route一个Layer会有多个层的情况processingfunctions,这多个处理函数构成了一个Route,Route中的每个function成为Route中的Layer。对于上面的代码,app.get('/test1',...)中的两个函数组成了一个Route,每个函数都是Route中的一个Layer。理解了以上概念后,再结合这张图,大概可以对整个过程有一个直观的体验。先启动服务,然后客户端发起请求http://localhost:3000/test2,流程应该如何运行?启动服务时,会依次执行程序,处理路由系统中的路径、请求方法、处理函数。Storage(信息按照一定的结构存放在Router、Layer、Route中)监听对应的地址,等待请求的到来。当请求到达时,首先根据请求的路径从上到下进行匹配。如果路径匹配正确,则进入该层,否则跳出该层。如果Layer匹配,则匹配请求方法,如果匹配方法正确,则执行对应Route中的函数。上面的解释比较简单,后面的细节部分会做进一步的阐述。3、体验细节通过以上对Express设计原理的分析,将从两个方面进一步解读源码。下面的流程图是一个普通的Express项目的流程。首先会初始化app实例,然后会调用一系列的中间件,最后创建一个监听器。对于整个项目的运行,主要分为两个阶段:初始化阶段和请求处理阶段。下面将以app.get()为例来说明核心细节。3.1初始化阶段让我们使用app.get()路由来了解项目的初始化阶段。1.先看app.get()的内容(源码中的app.get()是遍历方法生成的)app.get=function(path){//...this.lazyrouter();varroute=this._router.route(path);route.get.apply(route,slice.call(arguments,1));returnthis;};2.在app.lazyrouter()中,会完成router的实例化过程:this.enabled('strictrouting')});//这里会用到一些中间件this._router.use(query(this.get('queryparserfn')));this._router.use(middleware.init(this));}};注意:这个过程实际上使用了单例模式,保证整个过程中获取路由器实例的唯一性。3、调用router.route()方法完成图层的实例化、处理和存储,并返回实例化的路由。(注意源码是proto.route)router.prototype.route=functionroute(path){varroute=newRoute(path);varlayer=newLayer(path,{sensitive:this.caseSensitive,strict:this.strict,end:true},route.dispatch.bind(route));layer.route=route;//将路由放在层上this.stack.push(layer);//将层放入数组中returnroute;};4.app.get()中的函数存储在路由的堆栈中。(注意源码也是通过遍历方法挂载get到路由的原型)Route.prototype.get=function(){varhandles=flatten(slice.call(arguments));for(vari=0;i
