主题
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."