coderz blog

express

2021-05-21

express

本节一起来探究node中常用的express核心中的5个问题

1. 调用 express() 到底创建了什么❓

打开express core的 lib/express.js 会发现有这么一段代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
exports = module.exports = createApplication;
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;
}

很明显在调用 express()本质是执行了一段 createApplication 函数

2. app.listen()做了哪些事情❓

这里你可能会有两个疑问:

2.1 app不是一个函数吗❓为什么可以去调用 listen

app虽然是一个函数,其实也是一个对象,故而可以去调用 listen

2.2 在createApplication中并没有发现app中有listen函数

app可以直接调用listen 这里有一段很重要的处理即:

1
mixin(app, proto, false); //底层做了混入处理

proto 本质是 application,在 application.js中其本质就是调用了 http.createServer(this).listen 把参数全部传递了过来;
依据:

1
2
3
4
5
app.listen = function listen() {
var server = http.createServer(this);
return server.listen.apply(server, arguments);
};

3. app.use(中间件)内部发生了什么❓

open application.js 在 app.use = function use() 函数中生成了一个全局路由 router,在 var fns = flatten(slice.call(arguments, offset)) 做了扁平处理,将所有函数赋值给了 fns,接着通过 fns.forEach()取出每一个函数在228行本质调用了 router.use()函数

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
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
router.use(path, function mounted_app(req, res, next) {
var orig = req.app;
fn.handle(req, res, function (err) {
setPrototypeOf(req, orig.request)
setPrototypeOf(res, orig.response)
next(err);
});
});
````
跳到lib/router/index.js你的疑问即可揭晓。在其底层proto.use = function use(fn)中 其实是` new Layer()` ,然后将生成的图层 layer push到了 stack中,那么这个 stack就是初期绑定到router原型上的一个 stack数组,layer中我们的回调函数,`this.stack.push(layer)`这里的 this本质就是router对象。

### 4. 用户发送了请求,中间件是怎么被回调的❓
当一个请求过来后,底层本质是处理了 router中的handle函数 `router.handle(req, res, done);` 在 `proto.handle()` 函数中 通过self.stack拿到我们的 stack,第一次主动调用 next() 核心函数:
```javascript
// idx = 0 ; // init
function next(err) {
var layerError = err === 'route'
? null
: err;

// remove added slash
if (slashAdded) {
req.url = req.url.substr(1);
slashAdded = false;
}

// restore altered req.url
if (removed.length !== 0) {
req.baseUrl = parentUrl;
req.url = protohost + removed + req.url.substr(protohost.length);
removed = '';
}

// signal to exit router
if (layerError === 'router') {
setImmediate(done, null)
return
}

// no more matching layers
if (idx >= stack.length) {
setImmediate(done, layerError);
return;
}

// get pathname of request
var path = getPathname(req);

if (path == null) {
return done(layerError);
}

// find next matching layer
var layer;
var match;
var route;

while (match !== true && idx < stack.length) {
layer = stack[idx++];
match = matchLayer(layer, path);
route = layer.route;

if (typeof match !== 'boolean') {
// hold on to layerError
layerError = layerError || match;
}

if (match !== true) {
continue;
}

if (!route) {
// process non-route handlers normally
continue;
}

if (layerError) {
// routes do not match with a pending error
match = false;
continue;
}

var method = req.method;
var has_method = route._handles_method(method);

// build up automatic options response
if (!has_method && method === 'OPTIONS') {
appendMethods(options, route._options());
}

// don't even bother matching route
if (!has_method && method !== 'HEAD') {
match = false;
continue;
}
}

// no match
if (match !== true) {
return done(layerError);
}

// store route for dispatch on change
if (route) {
req.route = route;
}

// Capture one-time layer values
req.params = self.mergeParams
? mergeParams(layer.params, parentParams)
: layer.params;
var layerPath = layer.path;

// this should be done for the layer
self.process_params(layer, paramcalled, req, res, function (err) {
if (err) {
return next(layerError || err);
}

if (route) {
return layer.handle_request(req, res, next);
}

trim_prefix(layer, layerError, layerPath, path);
});
}

找到之后又调用了 layer 的handle_request()函数,这个函数又在处理什么呢?接着往下看。其实他做的事情本质上是调用核心fn(req, res, next);
fn是什么?
fn就是从 layer中取出的 handle

5. next为什么会自动执行下一个中间件❓

理解了第4个问题流程,当我们自己注册了中间件,调用了 next() 框架底层就会来到next()匹配下一个stack进行layer的执行

Tags: NodeJs
使用支付宝打赏
使用微信打赏

若你觉得我的文章对你有帮助,欢迎点击上方按钮对我打赏

扫描二维码,分享此文章