主题
Express源码解读02-创建应用
1.目录结构
|-- lib 框架核心代码
| |-- application.js 应用app核心代码
| |-- express.js 创建应用如口文件
| |-- request.js 请求相关
| |-- response.js 响应相关
| |-- utils.js 工具
| |-- view.js 模板渲染相关
| |-- middleware express中间件模块
| | |-- init.js
| | |-- query.js
| |-- router express路由模块
| |-- index.js 路由模块如口
| |-- layer.js
| |-- route.js
|-- index.js 入口文件
|-- package.json 框架的package.json记录文件2. 准备工作
express暴露的是一个函数,在使用时候,是通过express()来创建一个应用app的express作者在封装它的时候,用的都是函数的方式,一方面考虑了兼容性,一方面考虑了函数的方式模拟的类,既可以当做函数使用,也可以像class一样的使用,但是,class的方式创建的类,不可以直接调用。
3. express()过程解析
我们知道,express()其实就是创建一个express应用,函数的调用会得到一个app的返回值,下面就来分析一下创建应用的过程。
package.json中,没有main字段,那么在引入的时候,会找到根目录下面的index.js文件。
在index.js文件中就一行代码
js
module.exports = require('./lib/express');它先引入lib下面的express.js文件,然后再将其导出。
那么我们就来看express.js文件。
1. createApplication
js
exports = module.exports = createApplication;express.js文件内首先做的一件事就是导出一个函数createApplication,字面意思就是创建应用。那么可以知道,express.js文件的作用就是创建应用。
createApplication代表的就是我们require进去的express,同理exports=module.exports=createApplication就说明,exports和module.exports都同时指向同一个引用空间,所以下文中用到的exports指的就是express本身。
接着来看createApplication函数的内容
js
var mixin = require("merge-descriptors"); // 合并对象
var EventEmitter = require("events").EventEmitter;
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, EventEmitter.prototype, false);
mixin(app, proto, false); // false 不合并静态属性,
// expose the prototype that will get set on requests
app.request = Object.create(req, {
app: { configurable: true, enumerable: true, writable: true, value: app },
});
// expose the prototype that will get set on responses
app.response = Object.create(res, {
app: { configurable: true, enumerable: true, writable: true, value: app },
});
app.init();
return app;
}上面代码可以看出,函数导出的app本身是一个函数,并且,app混入了EventEmitter是原型,也就是说app拥有了EventEmitter公有的属性及方法,比如Node里面的发布订阅模式以及一些事件。 
proto是application文件,是具体的应用,也就是创建应用的主线,这个稍后再说。
最后,还在app上面挂载了request和response两个对象,这两个对象是用Object.create分别对原生req和res对象进行扩展,这也就是express的req和res拥有原生req和res上面方法的的原因。
2. app上属性代理到express上
上一步createApplication里面,通过对象合并的方式,在app上挂载了application,req,res对象里面的属性和方法,此时可以通过express直接调用这些属性和方法。
但在源码里面有下面这样的代码。
js
// 导出原型
exports.application = proto;
exports.request = req;
exports.response = res;这三行代码的意思是,express对象上扩展了application、request、response属性,那么此时我们不仅仅可以通过app.xxx来访问app上面的属性或方法,也可以直接通过express.application.xxx来访问。也就是一种代理的思想。
3. 在express上挂载路由
在express中默认提供了路由系统,不用我们当独下载,路由系统是express中比较核心且较难的点,后面会当独对其进行分析说明。 express.Router和express.Route中的路由就是在这里被挂载上去的。
js
// 导出构造函数
exports.Route = Route;
exports.Router = Router;4. express内置的中间件
express内置了一些中间件,但在新版本里面也移除了一些之前版本内置的中间件。
4.1 内置的中间件
js
// express上挂载中间件 (内置的中间件)
exports.json = bodyParser.json; // express.json()
exports.query = require("./middleware/query"); // express.query()
exports.raw = bodyParser.raw; // express.rwa()
exports.static = require("serve-static"); // express.static()
exports.text = bodyParser.text; // express.text()
exports.urlencoded = bodyParser.urlencoded; // express.urlencoded()从上面可以看出,express内置的中间件基本上是一些处请求的,比如,express.json()是将请求里面的body以json的形式解析后挂载到req.body上,express.static()中间件是借助serve-static中间件托管静态文件的,等等
4.2 被移除内置的中间件
express中也移除了很多的中间件,若是我们要使用他们,必须得通过npm install xxx方式下载后才能使用。被移除的中间件如下:
js
var removedMiddlewares = [
"bodyParser",
"compress",
"cookieSession",
"session",
"logger",
"cookieParser",
"favicon",
"responseTime",
"errorHandler",
"timeout",
"methodOverride",
"vhost",
"csrf",
"directory",
"limit",
"multipart",
"staticCache",
];express对已经移除的这些中间件做了提示,例如:用于通过express.bodyParser的方式使用bodyParser中间件,则会提示让你下载,但是bodyParser中间件不需要下载就能直接使用,是因为express内部已经使用过(上面的express一些内置的中间件就是用的bodyParser),默认就包含这个中间件,其他中间件就得下载后在使用。
js
removedMiddlewares.forEach(function (name) {
Object.defineProperty(exports, name, {
get: function () {
throw new Error(
"Most middleware (like " +
name +
") is no longer bundled with Express and must be installed separately. Please see https://github.com/senchalabs/connect#middleware."
);
},
configurable: true,
});
});若通过express.multipart()使用multipart中间件,则会得到下面错误提示,意思就是先下载,在使用。
Most middleware (like multipart) is no longer bundled with Express and must be installed separately. Please see https://github.com/senchalabs/connect#middleware."