Skip to content

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. 准备工作

  1. express暴露的是一个函数,在使用时候,是通过express()来创建一个应用app
  2. 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就说明,exportsmodule.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里面的发布订阅模式以及一些事件。 image.png

protoapplication文件,是具体的应用,也就是创建应用的主线,这个稍后再说。

最后,还在app上面挂载了requestresponse两个对象,这两个对象是用Object.create分别对原生reqres对象进行扩展,这也就是expressreqres拥有原生reqres上面方法的的原因。

2. app上属性代理到express上

上一步createApplication里面,通过对象合并的方式,在app上挂载了applicationreqres对象里面的属性和方法,此时可以通过express直接调用这些属性和方法。

但在源码里面有下面这样的代码。

js
// 导出原型
exports.application = proto;
exports.request = req;
exports.response = res;

这三行代码的意思是,express对象上扩展了applicationrequestresponse属性,那么此时我们不仅仅可以通过app.xxx来访问app上面的属性或方法,也可以直接通过express.application.xxx来访问。也就是一种代理的思想。

3. 在express上挂载路由

express中默认提供了路由系统,不用我们当独下载,路由系统是express中比较核心且较难的点,后面会当独对其进行分析说明。 express.Routerexpress.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()是将请求里面的bodyjson的形式解析后挂载到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."