这两天细致看了看express的源码,对其的悉数完成有了较清楚的熟悉,所以想总结一下写出来,如果有什么不对的处所,望指出。
这是第一篇,起首引见一个最简朴的express
运用运转历程,开端剖析了其在源码中的详细完成,还没有触及到一些比较主要的内容比方路由组件的完成体式格局,中心件的触发流程等。在后续的总结中,我会继续剖析,并预备将一些值得剖析的public api
一一解读,也会触及一些private api
。
基于的版本
停止写这篇文章时现在最新的tags是4.4.2
。我是直接看的master分支。express
的commits提交异常频仍,但整体的完成思绪应当不会有大的变化。其在4.x后做了较大的修改,相对于3.x最大的处所在于不再依靠connect
,并移除了险些一切的内置中心件,详细的更改请看官方wiki的 Migrating from 3.x to 4.x 及 New features in 4.x。
从一个官方示例最先
var express = require('express');
var app = express();
app.get('/', function(req, res){
res.send('Hello World');
});
app.listen(3000);
这是官方给出的一个简朴顺序,运转后接见localhost:3000
显现Hello World。下面我们就来细致看看这段顺序。
起首第一行
var express = require('express');
这是典范的Node.js模块载入代码,关于Node.js
的模块载入机制,不相识的同砚发起看看朴灵的深切Node.js的模块机制,异常有协助。
第一行载入了express
框架,我们来看源代码中的index.js
。
module.exports = require('./lib/express');
好吧,还要继续require,我们看./lib/express.js
exports = module.exports = createApplication;
从这里我们可以看出,顺序的第一行express
末了现实是这个createApplication
函数。第二行则是运转了这个函数,然后返回值赋给了app
。该函数代码以下
var EventEmitter = require('events').EventEmitter;
var mixin = require('utils-merge');
var proto = require('./application');
var req = require('./request');
var res = require('./response');
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
的'main'
函数,个中完成了一切建立express
实例所须要的行动,并在实行终了后返回一个函数。
代码的最先定义了一个函数,函数有形参req
,res
,next
为回调函数。
函数体只要一条语句,实行app.handle
,handle
要领在application.js
文件中定义,此处是经由过程mixin
导入(见下文),handle
的代码以下
app.handle = function(req, res, done) {
var router = this._router;
// final handler
done = done || finalhandler(req, res, {
env: this.get('env'),
onerror: logerror.bind(this)
});
// no routes
if (!router) {
debug('no routes defined on app');
// generate error
var err = new Error('No routes or middlewares have been defined');
err.status = 500;
done(err);
return;
}
router.handle(req, res, done);
};
它的作用就是将每对[req,res]
举行逐级分发,作用在每一个定义好的路由及中心件上,直到末了完成,详细的历程我们会在后续举行剖析。
然厥后看看中心的两行
mixin(app, proto);
mixin(app, EventEmitter.prototype);
mixin
是在头部的require处载入的utils-merge模块,它的代码以下
exports = module.exports = function(a, b){
if (a && b) {
for (var key in b) {
a[key] = b[key];
}
}
return a;
};
很明显,mixin(app, proto);
的作用等于将proto
中一切的property悉数导入进app
,proto
在头部的require处载入的是./lib/application.js
文件,个中定义了大部分express
的public api
,如app.set,app.get,app.use…详见官方的API文档。mixin(app, EventEmitter.prototype);
则将Node.js
的EventEmitter
中的原型要领悉数导入了app。
再来看接下来的两行
app.request = { __proto__: req, app: app };
app.response = { __proto__: res, app: app };
这里定义了app
的request
和response
对象,使用了对象的字面量示意法,使其离别继续自req
(顶部导入的request.js
)和res
(顶部导入的response.js
),并反向援用了app
本身。为何要如许做呢?这个题目我一最先想不明白,厥后我就痛快把这两行代码删了,运转,固然就是报错,答案就在毛病中的信息里。
TypeError: Object # has no method ‘send’
显现找不到'send'
要领,为何呢?起首我们从app.get()
要领看起,不熟悉的人会找不到它在源码中的位置,实在它在application.js
中是如许的
methods.forEach(function(method){
app[method] = function(path){
if ('get' == method && 1 == arguments.length) return this.set(path);
this.lazyrouter();
var route = this._router.route(path);
route[method].apply(route, [].slice.call(arguments, 1));
return this;
};
});
methods
在顶部模块引入中定义,现实上是一个包含各个HTTP
要求要领的数组,详细代码在这里。
从上面的代码中我们可以看到,这里现实上是遍历了一切methods中定义的要领,固然个中包含get
,而且get
要领是被’重载’的,即当app.get();
的参数只要一个时刻,实行的是猎取变量的功用,不然,实行route
组件中的route.get
要领,将该路由和回调函数(即第二个参数)存储进一个栈中(后续会进一步剖析)。
回到本来的题目,在这里,关键是看中心的
this.lazyrouter();
我们看它的详细代码
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._router.use(middleware.init(this));
}
};
它的作用是在第一次定义路由的时刻初始化路由(增加基础的路由),注重末了一句用到了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
,可以看到个中用到了我所迷惑app.request
和app.respone
,它使req
和res
继续自了request.js
和response.js
中的定义,也因此在我去掉了那两行代码后会涌现res.send
找不到的状况。
别的,定义app.response
对象时反援用本身,也使得背面在response
对象中可以经由过程this.app
取得所建立的express
实例。
让我们回到createApplication
函数,接下来是app.init();
。明显,作用是初始化,做哪些事情呢?
app.init = function(){
this.cache = {};
this.settings = {};
this.engines = {};
this.defaultConfiguration();
};
设定了cache对象(render的时刻用到),种种setting的存储对象,engines对象(模板引擎),末了举行默许的设置,代码有点长这里就不上了,就是做一些默许的设置。
好了,createApplication
函数就是这些,固然,个中略去了许多主要的题目,比方路由组件的完成体式格局,中心件的触发流程等,这我会在后续的总结中举行剖析。
最开首的官方示例中另有末了一句
app.listen(3000);
代码以下
app.listen = function(){
var server = http.createServer(this);
return server.listen.apply(server, arguments);
};
现实上是调用了Node.js
原生的http
模块的CreatServer
要领,API文档申明是
http.createServer([requestListener])#
Returns a new web server object.The requestListener is a function which is automatically added to the ‘request’ event.
要领返回的是一个web server
对象,个中的参数为HTTP request
事宜触发后实行的函数(这里我们给的就是我们在createApplication
函数中取得的app)。
末了,返回的web server
有一个监听端口的listen
要领,参数为须要监听的端口号,本示例中即为3000
。