express4.X源码解读第一天
express4.X 跟3.X 有很大区分,4.X 去除了connect的依靠,3.X基于connect的中间件基础悉数不能用,假如另有可以运用的,也是4.X重写的。所以要想继承运用这些熟习的中间件,就要手动装置依靠包,或许用一些其他的中间件。
下面最先源码解读
1. express是什么
typeof express === 'function' //true
可以晓得express是个函数,这个函数是顺序启动就会运转起来
function createApplication() {
var app = function(req, res, next) {
app.handle(req, res, next);
};
mixin(app, proto);
mixin(app, EventEmitter.prototype);
app.request = { __proto__: req, app: app };
app.response = { __proto__: res, app: app };
app.init();
return app;
}
上面这个函数就是express,有无看到很熟习的东西,看到app没,还在那里看到过这个熟习的东西…
对了,没错。就是每一个nodejs教程内里最先nodejs教授教养的事例,nodejs启动效劳器:http.createSever 的回调函数。app是express贯串全部流程的函数。实在全部express 实行历程就是往req,res这两个对象不断的修正属性,增添属性。直到完成要求。中间件也就是经由过程app做为回调,进而修正req,res。从而完成可插拔的结果。
var app = express();
这就是为何引入express,都要最先实行一下这个函数。
2. 顺序是怎样启动的
express做为一个web框架,起首要有启动一个效劳器的,我们看下效劳器是在那里启动的
var server = app.listen(app.get('port'), function() {
debug('Express server listening on port ' + server.address().port);
});
express用了一个我不太喜好用的写法,他把一切的要领直接放到app这个函数上去了,人人都晓得函数在js中就是对象,除了自身是可以实行之外,和对象是没有什么区分的。不过这就无形之中增添了浏览代码的难度,而且很轻易殽杂,因为app既做为一个中间件,还要做为一个大众要领的载体。
好了,讲到启动效劳器,app是没有启动效劳器的才能的,这个才能是在application 这个文件中被mix进去的,实在就是mix一个http.createServer要领,然则这里照样要看一下代码。
app.listen = function(){
var server = http.createServer(this);
return server.listen.apply(server, arguments);
};
看到this没有啊,这个this很主要哈,this == app 。app做为回调已传进来了,奇异的中间件在这里最先了路程。
3,从进口最先剖析源码
function createApplication() {
var app = function(req, res, next) {
app.handle(req, res, next);
};
mixin(app, proto);
mixin(app, EventEmitter.prototype);
app.request = { __proto__: req, app: app };
app.response = { __proto__: res, app: app };
app.init();
return app;
}
起首是把application模块的属性悉数mix进app内里去,在把事宜的属性悉数mix进app内里去,这是为了给app增添事宜功用。
然后把 req,res模块离别赋值给app,如许this是可以直接挪用request,response,详细实行历程照样到了app.init内里去看。
末了把顺序实例app返回出去了
好,下面到了application模块的init要领内里去了
app.init = function(){
this.cache = {};
this.settings = {};
this.engines = {};
this.defaultConfiguration();
};
增添了cache setting engines 三个对象,如今看不出来作用,详细实行历程到defaultConfiguration内里看看
this.enable('x-powered-by')
看到了enable,然后进去看enable实在就set,只不过第二个参数是boolean。set是什么呢?还记得我们没有相识功用的三个对象之一的setting,这个set就是往setting对象增添一些属性罢了。
好 先看defaultConfiguration
this.enable('x-powered-by')
设置x-powered-by 为true,x-powerd-by是什么意义呢?
有些查询东西在我们输入某个站点的URL后就可以推断这个站点的WebServer与顺序范例。
就是在http要求的时刻,可以看到x-powered-by:Express,不设置 就看不到效劳区范例,这应该是http要求的一部分
this.set('etag', 'weak');
这里处置惩罚etag的 Express依靠了一个叫etag的包
var env = process.env.NODE_ENV || 'development';
this.set('env', env);
this.set('query parser', 'extended');
this.set('subdomain offset', 2);
this.set('trust proxy', false);
这里继承设置属性。
// inherit protos
this.on('mount', function(parent){
this.request.__proto__ = parent.request;
this.response.__proto__ = parent.response;
this.engines.__proto__ = parent.engines;
this.settings.__proto__ = parent.settings;
});
// setup locals
this.locals = Object.create(null);
// top-most app is mounted at /
this.mountpath = '/';
// default locals
this.locals.settings = this.settings;
// default configuration
this.set('view', View);
this.set('views', resolve('views'));
this.set('jsonp callback name', 'callback');
if (env === 'production') {
this.enable('view cache');
}
Object.defineProperty(this, 'router', {
get: function() {
throw new Error('\'app.router\' is deprecated!\nPlease see the 3.x to 4.x migration guide for details on how to update your app.');
}
});
这里的mount,我之前不晓得什么意义,后来看其他运用才晓得,这是用来挂载其他运用的,比方我有几个运用,可以起几个营业效劳,用一个中心效劳监听端口,然后挂载其他几个运用模块
下面研讨一下app.use这个要领
研讨发明这个时刻express的初始化流程已走完了,之前看过3.X的源码,貌似不是如许子的,然则仔细观察,确确实实到这里是完毕了。盈余的要领都是怎样处置惩罚的呢?在细细往下看吧
add middleware to the app router
这是源码内里的诠释,向路由增添中间件,前面说过中间件和路由没有本质区分,是一样的东西。
app.use = function use(fn) {
var offset = 0;
var path = '/';
var self = this;
// default path to '/'
// disambiguate app.use([fn])
if (typeof fn !== 'function') {
var arg = fn;
while (Array.isArray(arg) && arg.length !== 0) {
arg = arg[0];
}
// first arg is the path
if (typeof arg !== 'function') {
offset = 1;
path = fn;
}
}
var fns = flatten(slice.call(arguments, offset));
if (fns.length === 0) {
throw new TypeError('app.use() requires middleware functions');
}
// setup router
this.lazyrouter();
var router = this._router;
fns.forEach(function (fn) {
// non-express app
if (!fn || !fn.handle || !fn.set) {
return router.use(path, fn);
}
debug('.use app under %s', path);
fn.mountpath = path;
fn.parent = self;
// restore .app property on req and res
router.use(path, function mounted_app(req, res, next) {
var orig = req.app;
fn.handle(req, res, function (err) {
req.__proto__ = orig.request;
res.__proto__ = orig.response;
next(err);
});
});
// mounted an app
fn.emit('mount', self);
});
return this;
};
因而我们看到lazyrouter这么个东西,这个函数内里new 了一个Router对象,所以这一张临时略过了 我们要去route内里看看了
昨天看源码遇到了贫苦,发明许多代码还不是那末轻易看懂,有些含糊,然后犯了一些毛病,打了许多断点终究弄清楚了
想要邃晓express的处置惩罚流程,必需先要弄清楚app.use和 app.handle这两个要领,这两个要领很主要。
前面我们已晓得app自身是做为回调参数传进http.createServer内里的,运用一切的路由都邑掉进这个函数内里去,经由一个一个中间件举行处置惩罚。自身想一想不是很庞杂,但看起代码来照样很蛋疼的
起首req,res被封装了许多要领进去,然则这个要领是在什么处所mix进去的呢。在这里我就犯了个毛病,毛病的以为会在use的时刻就会有这个要领,所以我在use函数内里找啊找,打了许多个断点,一直没有找到那里实行了这个操纵。
但实际上,use一直没有做这个操纵,use的作用就是route内里把这个回调push进route实例的stack内里,看代码
if (!fn || !fn.handle || !fn.set) {
return router.use(path, fn);
}
app的use实行了 Route实例的use。继承看Route的use
var layer = new Layer(path, {
sensitive: self.caseSensitive,
strict: false,
end: false
}, fn);
layer.route = undefined;
self.stack.push(layer);
去看会发明route的use和app的use会有些反复的代码,差别的处所就在于Route的use会建立一个layer。这个layer就是个实例,就是每一个回调函数的实例。这个实例包含全局设置的一些属性,比方严厉婚配,大小写。另有就是把当前use的路由url和回调存储起来了,悉数push进stack内里去。
看下route的实例化历程,会发明express默许安排了两个中间件进去。代码以下
app.lazyrouter = function() {
if (!this._router) {
this._router = new Router({
caseSensitive: this.enabled('case sensitive routing'),
strict: this.enabled('strict routing')
});
this._router.use(query(this.get('query parser fn')));
this._router.use(middleware.init(this));
}
};
所以app默许就会有两个中间件,query和 middleware。顺序实行到这里已实行完毕了。
那又有题目了,request,response这两个对象的许多扩大要领,从何而来。
下面就来看看吧
翻开middleware/init
exports.init = function(app){
return function expressInit(req, res, next){
if (app.enabled('x-powered-by')) res.setHeader('X-Powered-By', 'Express');
req.res = res;
res.req = req;
req.next = next;
req.__proto__ = app.request;
res.__proto__ = app.response;
res.locals = res.locals || Object.create(null);
next();
};
};
这里就看到了 request,response是在这里被安排到回调的req,res上去的。因为内置的这两个中间件是起首增添的,被安排在stack的前两个,所以每一个要求进来起首会进入这两个中间件内里去,然后带了许多东西进入其他的中间件去。
另有题目啊,use不是可以增添路由吗 不是可以掌握哪一些中间件走哪一些路由嘛,那是怎样掌握的呢。看这里。。。
proto.match_layer = function match_layer(layer, req, res, done) {
var error = null;
var path;
try {
path = parseUrl(req).pathname;
if (!layer.match(path)) {
path = undefined;
}
} catch (err) {
error = err;
}
done(error, path);
};
这里会把layer内里存储的route正则拿来和当前路由婚配,胜利则进入回调实行,失利则继承实行。