您的位置:首页 > Web前端 > Node.js

node平台express框架源码分析-1

2016-07-29 14:50 441 查看
首先给一个官网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 proto
4000
s
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的调用栈中

根据上述分析可见express将用户的请求处理分为两个阶段,

第一阶段以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方法的分析下节继续分析。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: