施工中……
express构造的是Http.createServer的回调函数
express是一个基于NodeJS的框架,先来看下如果不使用框架要创建一个最简单的web应用应该是怎么样
1 | const http = require('http'); |
实际上express是一个函数,运行后可以构造出上面代码中http.createServer的回调函数,express做的一切文章都是在这个回调函数上。来看下express3.x的源码express.js
1 | //========== 你的应用 app.js ================== |
connect.js的具体内容先不关心,后面会重点介绍。可以看出connect是一个函数,运行返回一个app,app是一个形如function(req, res , next){ … } 的函数。express的createApplication返回即是此app,用于http.createServer的回调。并在这个函数上混入的许多能力,如req、res的处理、模板引擎、静态文件服务、router的能力。

用比较简单的伪代码表示如下
1 | const app = express(); |
上面的1、2、3、4顺序即为开发者注册时的顺序(故我们平时在开发时express注册中间件时是有先后顺序的)。express最主管理与运行中间件的能力,接下来深入内部看看connect这个中间件机制是怎么实现的。
最为核心的中间件框架
1 | //connect.js 的简要内容 |
不难看出,app.use中间件时,只是把它放入一个数组中。当http请求时,app会从数组中逐个取出,进行匹配过滤,逐个运行。遍历完成后,运行finalhandler,结束一个http请求。可以从http请求的角度思考,一次请求它经历经历了多少东西。express的这个中间件架构就是负责管理与调用这些注册的中间件。中间件顺序执行,通过next来继续下一个,一旦没有继续next,则流程结束。
接下来提一下异步编程的串行控制,加强理解;
异步串行流程控制
为了用串行化流程控制让几个异步任务按顺序执行,需要先把这些任务按预期的执行顺序放 到一个数组中。如图,所示,这个数组将起到队列的作用:完成一个任务后按顺序从数组中取 出下一个

数组中的每个任务都是一个函数。任务完成后应该调用一个处理器函数,告诉它错误状态和 结果。如果有错误,处理器函数会终止执行;如果没有错误,处理器就从队列中取出下一个任务 执行它
下面是一个简单实现方案:
1 | // 数组 |
异步串行控制方案除了上面的这种以外,还可以用es6的promise的then链、async/await、yeild、社区工具等;
可以看到代码确实谈不上高级😂,串行导致的性能谈不上优秀,但是得益于此它足够简单易用。到此可以发现express的中间件架构就是一个中间件的的管理与数组遍历运行,这个方案就让社区形形色色各种各样的中间件很好的添加express能力,这点很简单也很重要,因为后续的路由、静态文件服务、代理等都是中间件,都在这个框架内运行。
Router是一个内置在app函数上的中间件
来看下简化后的router.js
1 | //express创建时运行 |
上面的usedRouter是个开关,未开启则不加入router中间件,因为应用理论上也是可能不用到router的。当app[method] 如app.get(‘/user’, fn)调用后,则触发this.use(this.router) 使用router中间件,同时把usedRouter设置为true。之后往router对象中加入fn回调函数。
router实际上也是一个异步串行流程控制,简化版的代码如下
1 | Router.prototype.addRoute = function(method, path, handles){ |
router跟connect非常类似,上述理解了connect,router就很清晰了。一图以蔽之:

实际上router还有细分,某个router还是可以继续做类似的串行流程控制;与中间件相同,每个router一旦停止了next,流程就结束了。
request经过router可以请求一个数据,或者一个网页;网页的话是怎么返回的呢,接下来看下view的render;
视图-模板引擎
模板引擎是根据对模板结合data进行运行处理,生产real html;这跟React、Vue、模板引擎是类似的。模板引擎不是express 实现的,实际上express仅仅只是做了调用;这里有个通用的支持各种模板引擎的模块consolidate.js
1 | var cons = require('consolidate') |
express要做的只是配置与调用;
1 | // express设置属性 |
通过这两个函数设置views视图所在的路径、模板引擎类型,之后express就可以结合router提供的render page,data,render callback的数据进行视图渲染
1 | app.render = function (name, options, fn) { |
为了性能考虑还做了cache;关于模板引擎,实际上很简单,读者可以自定一个模板引擎规则。
静态文件服务
静态文件服务也是一个中间件,express做的事情也仅仅是引用。require一个serve-static,内置在app函数上。
1 | app.static = function (dir) { |
当调用app.static时就会把静态文件服务中间件放入stack中,这里与express调用方式稍有不同,因为笔者觉得这么写更好更简单。
简单实现:
1 | /**仿照express实现中间件的功能 Created by haoxin on 2018/7/9.*/ |