express
本节一起来探究node中常用的express
核心中的5个问题
1. 调用 express() 到底创建了什么❓
打开express core的 lib/express.js 会发现有这么一段代码
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 这里有一段很重要的处理即:
mixin(app, proto, false); //底层做了混入处理
复制代码
proto
本质是 application,在 application.js中其本质就是调用了 http.createServer(this).listen 把参数全部传递了过来;
依据:
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()函数
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() 核心函数:
// 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的执行