express 精读

740 阅读3分钟

tj.jpg
先上传tj大神的照片,表示膜拜

刚知道,他是学设计的,刚知道他长得也这么设计,也刚听说,要想成为他一样的大神,要读源码。于是有了这篇文章。

express 框架

  • application, request, response, router 四大块
  • 核心的思想是中间件,基础是node的http模块
  • 中间件的基础是 layer, 核心是router

application


express 返回的 createApplication方法,在当在应用层执行const app = express()的时候,返回的还是一个函数 function(req, res, next){ ... app.handle ...},application 的作用就是在app上挂载,自己封装的request,response,mixin继承proto, EventEmitter.prototype对象方法。

 function createApplication() {
 var app = function(req, res, next) {
   app.handle(req, res, next);
 };

 mixin(app, EventEmitter.prototype, false);
 mixin(app, proto, false);

 // expose the prototype that will get set on requests
 app.request = Object.create(req, {
   app: { configurable: true, enumerable: true, writable: true, value: app }
 })

 // expose the prototype that will get set on responses
 app.response = Object.create(res, {
   app: { configurable: true, enumerable: true, writable: true, value: app }
 })
 app.init();
 return app;
}

中间件


  • application 的主要扩展在 proto,分析的时候主要抓住 app = handle(req, res, next)。由app.handle 到 router.handle,主要逻辑就在此。
  • node 模块http创建web服务类似 http.createServer(app).listen(80); 框架的重点是封装app。app就是中间件的有序执行集合。
  • 什么中间件的重点是 app.use 和 app[method]。
var express = require('./index');
var app = express();
// 声明中间件
app.use(function cc(req,res,next){
console.log("111");
next();
})

app.get('/users', function(req, res, next) {
console.log(11111)
next();
}, function(req, res, next) {
console.log(22222)
next();
}, function(req, res, next) {
console.log(33333)
next();
}, function(req, res, next) {
console.log(44444)
res.send('hello world')
})

var server = app.listen(8081, function() {
var host = server.address().address
var port = server.address().port
console.log("应用实例,访问地址为 http://%s:%s", host, port)
})

打印中间件:

image.png

注意:前两个中间件是框架自带,第三个是use声明的中间件,第四个是app[method]声明的。

  • 有序执行是声明的顺序 和 next 方法。 next 有两个 router/index.js 和 router/route.js 文件中各有一个next
    • 第一个next是上图中四个红色箭头指向的中间件依次执行的保证。
    • 第二个next是第四个箭头中route.stack各个layer顺序执行的保证。 app中的 res, req, next 就是这么一个layer 传到下一个layer。碰到route不等于undefined时候,在进入下一层 layer,直到最后一个中间件,调用res.send 终结返回,完成一个http请求。

router.handle里面 next() 函数是处理各种中间件的。while 循环遍历 stack(初始化所有中间件的数组)。遍历出所有符合当前路由中间件(layer), next 一个一个处理,然后返回。

response / request


相对比较简单,关键是看如何跟框架结合。

  • request.js 中 var req = Object.create(http.IncomingMessage.prototype); req 是基于node http模块当原型链重新声明 req。又在req自身属性上挂在一些自定义的常用到的方法。
  • 同理 response.js 中 var res = Object.create(http.ServerResponse.prototype); res 也是基于node http重新声明的。挂在自己的常用函数。 application中。自己封装的res, rep都是直接挂在到app静态属性上的。
    • 1: 是为了暴露出去,用户可以自行扩展。
    • 2: 是为了柯里化,传入到中间件中。 在每个声明中间件 无论使用use 还是什么app[method] 都需要先执行 lazyrouter
      image.png
      第一个use 默认的中间件是为了处理请求参数匹配路径 第二个use 是为了初始化res和req
      image.png

注意:其实并没有执行,只是先把app 传过去,app上挂在了,res, rep 返回了一个中间件,当用户请求的时候,先去执行这个中间件。

image.png

感悟


分析别人源码,是习惯,先掌握主要脉络反推用户的框架思想,提升格局。在逐行分析,理解框架写法的优雅。先抓大放小,再锱铢必较。