Express框架详解--- app 函数

2,831 阅读4分钟

首先要搞明白 express 是一个函数。

express 函数,函数也是个对象,意味着函数上也可以挂载自己的属性。

express 函数来源

module.exports = require("./lib/express");

exports = module.exports = createApplication;

function createApplication() {
  var app = function (req, res, next) {
    app.handle(req, res, next);
  };
  // ...
  return app;
}

此处省略中加的一些在这里无关的代码,我们看到 express 函数其实是一个函数,返回一个 app 函数。app 也很简单 app 中调用了 app.handle 方法,注意这里 app.handle 其实用来接收请求的。这里会在请求数据的时候体验到。现在目的是分析 app 函数的创建中会做哪些事情。

express 的其他输出

exports.application = proto; // application 的原型
exports.request = req; // 请求对象
exports.response = res; // 响应对象
exports.Route = Route; // 路由项目
exports.Router = Router; // 路由
exports.json = bodyParser.json; //解析 json
exports.query = require("./middleware/query"); // 查询
exports.raw = bodyParser.raw; // 生地址
exports.static = require("serve-static"); // 静态地址
exports.text = bodyParser.text; // 文本
exports.urlencoded = bodyParser.urlencoded; // 解析

移除中间列表中包含了,使用错误提示:

var removedMiddlewares = [
  "bodyParser",
  "compress",
  "cookieSession",
  "session",
  "logger",
  "cookieParser",
  "favicon",
  "responseTime",
  "errorHandler",
  "timeout",
  "methodOverride",
  "vhost",
  "csrf",
  "directory",
  "limit",
  "multipart",
  "staticCache",
];

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,
  });
});

app 方法中混入原型

var mixin = require("merge-descriptors");

mixin(app, EventEmitter.prototype, false);
mixin(app, proto, false);
  1. 混入 Node.js 的事件触发
  2. 混入 application 原型

app 中挂载请求/响应对象

var req = require("./request");
var res = require("./response");

app.request = Object.create(req, {
  app: { configurable: true, enumerable: true, writable: true, value: app },
});

app.response = Object.create(res, {
  app: { configurable: true, enumerable: true, writable: true, value: app },
});

从 req/res 对应的文件中,获取 req/res 两个不同对象。然后使用 Object.create 添加原型,然后创建了 value 是 app 的内容。这个操作的作用是在 res/req 两个对象中使用 app 函数以及挂载的对象。

app 中的原型

从源码中得知,app 的 proto 并不是使用 app.prototype.xxx 来进行扩展的,而是使用 mixin 方法来进行扩展的。这里我们要了解到 JS 的原型链的安全问题(防止原型链被污染,这些重要的方法),下面俩看看 merge-description 的实现方法:

"use strict";
module.exports = merge;
var hasOwnProperty = Object.prototype.hasOwnProperty;
function merge(dest, src, redefine) {
  // ...
  if (redefine === undefined) {
    redefine = true;
  }

  Object.getOwnPropertyNames(src).forEach(function forEachOwnPropertyName(
    name
  ) {
    if (!redefine && hasOwnProperty.call(dest, name)) {
      return;
    }
    var descriptor = Object.getOwnPropertyDescriptor(src, name);
    Object.defineProperty(dest, name, descriptor);
  });

  return dest;
}

多余的源码已经移除。本质就是把 src 对象上自己的属性描述符赋值给 dest 的属性描述符,实现了此次 merge 或者叫做 mixin。

app 对象上挂载方法

注意 app 上的方法不是通过 prototype 的方式挂载的,在原密码中被标记为 @private,是不被随意修改的。

  • 定义 app 方法
var app = (exports = module.exports = {});
  • 方法和属性说明
方法说明
defaultConfiguration初始化配置
lazyrouter(私有)如果没有初始化过 router, 会初始化一次 router
handle(私有)将 req、res 对分派到应用程序中。开始管道处理。
use代理"Router#use()"将中间件添加到应用路由器
engine注册模板引擎
param代理到"Router#param()",添加一个 api 功能。
set在 settings 对象设置一个 key-value
path返回一个 app 的绝对路径
enabled检查 settings 是否启动
disabled检查 settings 是否被禁用
enable设置 setting 是 true
disable设置 settings 是 false
app.[methods]按照 methods 中数组添加 app 上的 http 方法
all特殊情况的"all"方法,将给定的路由"路径"、中间件和回调应用于_every_ HTTP 方法。
del是 delete 的别名
render渲染指定名字的模板
listen监听链接

app 初始化

app.init = function init() {
  this.cache = {};
  this.engines = {};
  this.settings = {};

  this.defaultConfiguration();
};

初始化方法很简单,this 过载属性

  • cache
  • engines
  • settings 配置

调用初始化配置,其实就是 settings 山挂载很多属性:

  1. 获取环境变量
  2. 启动 x-powered-by
  3. 设置 etag 是 weak
  4. 设置环境变量
  5. 设置 query parser
  6. 设置 subdomain offset
  7. 设置 trust proxy
  8. 设置 this.settings trustProxyDefayultSymbol 的属性
  9. 添加 mount 监听函数
  10. 配置 locals
  11. 配置最好的 app 挂载到 /
  12. 配置 local.settings 为 this.settings
  13. 设置 view
  14. 设置 views
  15. 设置 jsonp callback name
  16. 在生产环境中启动 view cache
  17. 在 router 上挂载 方法排除 app.router 被废弃的放错误提示

这样一个 app 就初始化完成了。

小结

这篇文章主要是对于 application 对象的内容。首先有两个 app 对象:

  • 第一个是函数 app, 并且在 app 上挂载一些属性,将来会合并 原型 app 对象
  • 第二个 app 就是第一个 app 的原型对象,为而立原型链的安全没有使用 prootype 来实现方法,而是使用了私有方法来将俩个 app 进行合并。
  • 合并之后 app 初始上初始化了属性和方法。例如常用的 use/[http#methods]/listen常用的属性和方法。