用了这么久的express框架, 对它的原理还是不太清楚,惭愧惭愧, 赶紧学习一下。
express的主要功能有:
- 中间件的使用
- 路由
- 模板引擎
- 静态文件服务
- 设置代理
这篇文章主要涉及到express中间件的实现原理, 这也是面试的重头戏啦。
先看一个最简单的express例子:
1
2
3
4
5
6
7
8
9
|
var express = require('express');
var app = express();
app.listen(3000, function () {
console.log('listen 3000...');
});
app.use(middlewareA);
app.use(middlewareB);
app.use(middlewareC);
|
app.use()就是通常所说的使用中间件
什么是中间件?
一个请求发送到服务器后,它的生命周期是 先收到request(请求),然后服务端处理,处理完了以后发送response(响应)回去
而这个服务端处理的过程就有文章可做了,想象一下当业务逻辑复杂的时候,为了明确和便于维护,需要把处理的事情分一下,分配成几个部分来做,而每个部分就是一个中间件
app.use 加载用于处理http请求的middleware(中间件),当一个请求来的时候,会依次被这些 middlewares处理。
中间件执行的顺序是你定义的顺序.
中间件源码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
|
//connect.js 的简要内容
function createServer(){
// app是用于http.createServer的回调函数
function app(req, res, next){
// 运行时调用handle函数
app.handle(req, res, next);
}
mixin(app, proto, false);
// 初始化一个stack数组
app.stack = [];
return app;
}
// use调用时往app的stack数组中push一个对象(中间件),标识path与回调函数
proto.use = function(route, fn){
var path = route,
handle = fn;
//... 省略其他
this.stack.push({
route: path,
handle
});
};
// handle方法,串行取出stack数组中的中间件,逐个运行
proto.handle = function(req, res, out){
var index = 0;
var stack = this.stack;
var done = out || finalhandler(req, res, { onerror: logerror });
// 遍历stack,逐个取出中间件运行
function next(err){
var layer = stack[index++];
// 遍历完成为止
if(layer === undefined){
return done();
}
var route = pathFormat(layer.route);
var pathname = pathFormat(urlParser(req.url).pathname || '/');
// 匹配中间件,不匹配的不运行
if(route !== '' && pathname !== route){
next(err);
return;
}
// 调用中间件
call(layer.handle, err, req, res, next);
}
next();
};
|
不难看出,app.use中间件时,只是把它放入一个数组中。
当http请求时,app会从数组中逐个取出,进行匹配过滤,逐个运行。遍历完成后,运行finalhandler,结束一个http请求。
可以从http请求的角度思考,一次请求它经历经历了多少东西。
express的这个中间件架构就是负责管理与调用这些注册的中间件。
中间件顺序执行,通过next来继续下一个,一旦没有继续next,则流程结束。
总结
总的来说, express内部维护一个函数数组,这个函数数组表示在发出响应之前要执行的所有函数,也就是中间件数组。
使用app.use(fn)后,传进来的fn就会被扔到这个数组里,执行完毕后调用next()方法执行函数数组里的下一个函数,
如果没有调用next()的话,就不会调用下一个函数了,也就是说调用就会被终止。
并没有想象中的复杂吧。
参考资料
- NodeJS express框架核心原理全揭秘
- Express中间件的原理及实现