这两天仔细看了看express的源码,对其的整个实现有了较清晰的认识,因此想总结一下写出来,若是有什么不对的地方,望指出。html
这是第一篇,首先介绍一个最简单的express
应用运行过程,初步分析了其在源码中的具体实现,尚未涉及到一些比较重要的内容好比路由组件的实现方式,中间件的触发流程等。在后续的总结中,我会继续分析,并准备将一些值得分析的public api
逐一解读,也会涉及一些private api
。node
截止写这篇文章时目前最新的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。git
var express = require('express'); var app = express(); app.get('/', function(req, res){ res.send('Hello World'); }); app.listen(3000);
这是官方给出的一个简单程序,运行后访问localhost:3000
显示Hello World。下面咱们就来仔细看看这段程序。github
首先第一行web
var express = require('express');
这是典型的Node.js模块载入代码,关于Node.js
的模块载入机制,不了解的同窗建议看看朴灵的深刻Node.js的模块机制,很是有帮助。express
第一行载入了express
框架,咱们来看源代码中的index.js
。npm
module.exports = require('./lib/express');
好吧,还要继续require,咱们看./lib/express.jsapi
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
实例所须要的动做,并在执行完毕后返回一个函数。app
代码的开始定义了一个函数,函数有形参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
。