首先给一个官网express的例子,express的版本是4.14.0
var express = require('express');
var app = express();
app.get('/', function (req, res) {
res.send('Hello World!');
});
var server = app.listen(3000, function () {
var host = server.address().address;
var port = server.address().port;
console.log('Example app listening at http://%s:%s', host, port);
});
下面将分析上述例子中出现的几个API:
var app = express();
打开node_modules目录找到依赖的源码文件:./express/lib/express.js.该文件导出函数createApplication.
express()即调用了createApplication()函数,app变量是createApplication函数的返回值。
下面是createApplication函数的构造函数:
function createApplication() {
//创建app对象
var app = function(req, res, next) {
app.handle(req, res, next);
};
//继承node的事件对象
mixin(app, EventEmitter.prototype, false);
//继承./application对象
mixin(app, proto, false);
//app.request和response继承node原生的request和response对象
app.request = { __proto__: req, app: app };
app.response = { __proto__: res, app: app };
//初始化app对象
app.init();
return app;
}
app.init()方法调用的是继承自./application.js的方法。
下面是application.js中的init方法:
app.init = function init() {
this.cache = {};
this.engines = {};
this.settings = {};
this.defaultConfiguration();
};
下面是app.defaultConfiguration的源码分析:
app.defaultConfiguration = function defaultConfiguration() {
//初始化app的默认配置
......
......
//监听mount事件,当我们向express中添加中间件的时候会触发mount事件,
//这里会将每个中间件的request对象,response对象,engines对象,settings对象通过__proto__形成原型连,
//最顶层的request和response对象是Node原生的request和response对象,在createApplication中定义
this.on('mount', function onmount(parent) {
// inherit trust proxy
if (this.settings[trustProxyDefaultSymbol] === true && typeof parent.settings['trust proxy fn'] === 'function') {
delete this.settings['trust proxy'];
delete this.settings['trust proxy fn'];
}
// inherit protos
this.request.__proto__ = parent.request;
this.response.__proto__ = parent.response;
this.engines.__proto__ = parent.engines;
this.settings.__proto__ = parent.settings;
});
......
......
};
express()分析结束。
app.get('/', function (req, res) {
res.send('Hello World!');
});
该api的作用是创建到"/"的get请求的路由处理器,app.get/post/head等方法在application文件中的下述代码中定义:
methods.forEach(function(method) {
app[method] = function(path) {
if (method === 'get' && arguments.length === 1) {
// app.get(setting)
return this.set(path);
}
//给app对象绑定一个路由管理器Router
this.lazyrouter();
//使用路由管理器给指定的path创建一个路由对象处理对象
var route = this._router.route(path);
//调用路由处理对象的相应方法
route[method].apply(route, slice.call(arguments, 1));
return this;
};
});
下面分析下这行代码的实现:
var route = this._router.route(path);
在./router/index.js文件中定义的route方法:
proto.route = function route(path) {
//创建并初始化route对象
var route = new Route(path);
//创建一个layer对象,并放入栈中,
//在处理业务的时候会根据path的匹配规则,来匹配对应的route,
//如果是使用use接口(比如添加中间件的时候),是不指定route的,
var layer = new Layer(path, {
sensitive: this.caseSensitive,
strict: this.strict,
end: true
}, route.dispatch.bind(route));
layer.route = route;
this.stack.push(layer);
console.log("app.get(path,cb) return route:" + JSON.stringify(route));
return route;
};
route[method].apply(route, slice.call(arguments, 1));
在./router/route.js中定义的对应的method方法:
methods.forEach(function(method) {
Route.prototype[method] = function() {
var handles = flatten(slice.call(arguments));
for (var i = 0; i < handles.length; i++) {
var handle = handles[i];
if (typeof handle !== 'function') {
var type = toString.call(handle);
var msg = 'Route.' + method + '() requires callback functions but got a ' + type;
throw new Error(msg);
}
debug('%s %s', method, this.path);
var layer = Layer('/', {}, handle);
layer.method = method;
this.methods[method] = true;
this.stack.push(layer);
console.log("app.get(path,cb) cb values:" + handle + ";this.path is " + this.path + ";layer:" + JSON.stringify(layer));
console.log("app.get(path,cb) return route :" + JSON.stringify(this));
}
return this;
};
});
和route方法类似,区别是上下文变了,而且在layer中指定的回调函数是用户定义的回调。
app.get方法分析完毕,总结下 该方法主要完成了两件事:
- 根据path创建route对象,根据用户定义的回调事件创建layer对象,将layer对象放入route调用栈中
- 根据path创建layer对象,将创建的route对象绑定到layer上,将layer对象放入router的调用栈中
第一阶段以router为核心,将用户的请求在router的stack中做循环预处理(中间件处理,根据路由匹配规则)
第二阶段以route为核心,将用户的请求在route的stack中做循环处理(以method为匹配规则)
两阶段的联系是通过./router/route.js中的dispatch方法关联的。
在本节的最后一起来看下app.listen的实现(application.js中):
app.listen = function listen() {
var server = http.createServer(this);
return server.listen.apply(server, arguments);
};
其中的this指的是app对象,在前面express()中已经分析过app的原型了,其中app的构造函数就是:
var app = function(req, res, next) {
app.handle(req, res, next);
};
所以app.listen翻译下其实就是我们常见的下述写法:
app.listen = function listen() {
var server = http.createServer(function(req,res,next){
app.handle(req,res,next)
});
return server.listen(arguments);
};
app.handle的源码在./application.js中:
app.handle = function handle(req, res, callback) {
var router = this._router;
// final handler
var done = callback || finalhandler(req, res, {
env: this.get('env'),
onerror: logerror.bind(this)
});
// no routes
if (!router) {
debug('no routes defined on app');
done();
return;
}
router.handle(req, res, done);
};
该方法将监听到的用户请求转入router中处理,即第一阶段处理。
router.handle方法的分析下节继续分析。