阅读源码之前需要读者对javascript的this变量,闭包以及原型链有深刻理解,因为源码涉及很多对这方面知识运用,同时读者要对express的api比较了解,可以阅读这里来了解api的功能。 在读文章之前,请读者先下载express源码,跟着我的思路阅读代码。
首先来看看上面这个简单的例子,我们从简单入手。
第一行引入了
express
模块,我们进入
express
的
index.js
,看到如下代码
这里的程序只是引用
lib/express
文件,我们进入
lib/express
文件看看
图1程序的第一行
express
最后返回一个
createApplication
函数,第二行则是运行了这个返回的函数,然后返回值赋给了
app
,可以发现,这个就相当于
express
的
main
函数,其中完成了所有创建express实例所需要的动作。
图3中代码的开始定义了一个函数,并把函数返回给app变量,函数有形参
req,res,next
为回调函数,函数体只有一条语句,执行
app.handle,handle
方法在
application.js
文件中定义,此处是通过
mixin
导入(见下文),
handle
的代码如下
handle函数的作用就是函数的入口,具体的过程如下,我看看看图1这句代码
app.listen(3000);
。我们进入listen函数看看
从
http.createServer(this)
这句代码可以看出,一旦有请求访问3000这个端口,就会调用
this
,
this
指的是
app
, 程序会执行
app()
调用图3的
handle
方法,这里就是整个app的函数入口了,先把函数入口记住,等后面会回来讲解。
继续看图3的代码
app.request = { __proto__: req, app: app }; app.response = { __proto__: res, app: app };
这两句代码相当于给app定义了request,response
的属性,同时让这两个属性的原型指向req,res
var req = require('./request');
var res = require('./response');
这里的request和response不是我们此篇文章的重点。
我们接着图3的代码往下看
app.init();
进入init
函数看看
app.init = function init() {
this.cache = {};
this.engines = {};
this.settings = {};
this.defaultConfiguration();
};
/**
* Initialize application configuration.
* @private
*/
app.defaultConfiguration = function defaultConfiguration() {
var env = process.env.NODE_ENV || 'development';
// default settings
this.enable('x-powered-by');
this.set('etag', 'weak');
this.set('env', env);
this.set('query parser', 'extended');
this.set('subdomain offset', 2);
this.set('trust proxy', false);
// trust proxy inherit back-compat
Object.defineProperty(this.settings, trustProxyDefaultSymbol, {
configurable: true,
value: true
});
debug('booting in %s mode', env);
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;
});
// 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.');
}
});
};
这里的代码就是给app
做了一些初始化,给他设置了一些参数初始值。文章只讲express
一些主要的方法,不可面面俱到,想对这个init
函数的细节深入了解的需要读者自己去了解源码。比如set
函数和enable
函数。
读到这里,可能有很多读者不知道要如何继续往下读了,这里我给大家一个建议,读任何源码都可以从常用的api
入手,前面讲了那么多,却还没开始讲api
是如何定义的。说道express
的api
,相信所有人都会首先想到use
函数,那我们就来看看use
函数是如何定义的。代码如下。
use
函数,首先对传进来的参数做了验证,判断第一个参数是不是路径,如果是路径就取出后面的参数做遍历,如果不是路径就直接拿参数做遍历,在遍历之前先调用this.lazyrouter()
,我们来看看lazyrouter
是怎么定义的,代码如下。
先判段
_router
是否存在,不存在就新建一个
Router
,
caseSensitive
表示Router是否要对路径的大小写敏感