当前位置: 首页 > 文档资料 > 阅读 express 源码 >

2.3 查看 express.js 入口文件

优质
小牛编辑
134浏览
2023-12-01

先看导出了哪些

2.3.1 createApplication

设置默认导出了一个createApplication方法,这个就是我们整个程序的入口。

exports = module.exports = createApplication;

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

  mixin(app, EventEmitter.prototype, false);
  mixin(app, proto, 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;
}

首先我们看这段代码

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

诶,这就有点奇怪了,app 是一个函数,为什么又可以调用 app.handle 方法呢?哪来的,不会报错吗?

第一个问题:从哪来的 app.handle ?

看到 mixin 没,虽然我不知道这个 mixin 从哪来的(当然,我也不想关心 mixin 具体用法,至少此刻来说是的),根据我以往的经验来说 mixin 就是混合合并的意思(懂什么作用就行),它这里的意思就是合并 app 与 proto、EventEmitter.prototype。现在 app 就具有他俩的方法,而这个 handle 方法就是在 proto 上面。

第二个问题:不会报错吗?

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

app() // 报错

这样做确实是会报错,因为我们还没有 mixin 好,通常当我们调用的时候,mixin 已经混合完成,所以就不会报错。

当我们在实际项目中用起来就像这样,这个 app 与上面的 app 相同。

var createApplication = require('express');
var app = createApplication()

接下来看 app.request 与 app.response

  // 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 }
  })

Object.create() 指定原型对象和属性来创建一个新的对象。 更多细节点击这里查看

也就是说上面的这一行实现了以下功能

  • app.request.proto = req
  • app.request.app = app。

这个是属于原型链继承,众所周知,当一个 js 对象找不到属性的时候,它会往它的__proto__属性上面去寻找该属性,一直找,直到没有__proto__ 为止。

你可能会好奇 mixin 与 create 有什么区别呢?

我们来看看 mixin 的 merge-descriptors 的源码。

function merge(dest, src, redefine) {

  if (redefine === undefined) {
    redefine = true
  }

  Object.getOwnPropertyNames(src).forEach(function forEachOwnPropertyName(name) {
    if (!redefine && hasOwnProperty.call(dest, name)) {
      // dest 已经有 name 属性了
      // redefine = true 覆盖掉 dest 的 name 属性
      // redefine = false 保持原来 dest 的 name 属性
      return
    }

    // Copy descriptor
    var descriptor = Object.getOwnPropertyDescriptor(src, name)
    Object.defineProperty(dest, name, descriptor)
  })

  return dest
}
  • Object.prototype.hasOwnProperty() -- 返回一个布尔值 ,表示某个对象是否含有指定的属性,而且此属性非原型链继承的。 更多细节请点击

  • Object.getOwnPropertyDescriptor() -- 方法返回指定对象上一个自有属性对应的属性描述符。更多细节请点击

  • Object.defineProperty() -- 给对象添加一个属性并指定该属性的配置, 更多细节点击这里查看

所以说,mixin 是在原来的对象上面 copy 属性进行继承,而 create 是以原型链的方式继承。so 我们又 get 到继承的2种方式。

之后再调用了 app.init() , 这是我们进一步阅读的入口。

2.3.2 其他细枝末节
/**
 * Expose the prototypes.
 */

exports.application = proto;
exports.request = req;
exports.response = res;

它这里给出的注释是导出原型,紧接着我们来查看 proto 到底是什么?

移动到最上面, 发现这都是从其他文件里面导入的。现在我们暂时不看其他文件,先把入口文件给看完。

var proto = require('./application');
var Route = require('./router/route');
var Router = require('./router');
var req = require('./request');
var res = require('./response');

以及以下这些导出。

exports.Route = Route;
exports.Router = Router;

/**
 * Expose middleware
 */

exports.query = require('./middleware/query');
exports.static = require('serve-static');

这里的 Route 与 Router 、query 都是从其他文件导入的,而 static 是从 node_modules 依赖里面导入的。

[
  'json',
  'urlencoded',
  'bodyParser',
  'compress',
  'cookieSession',
  'session',
  'logger',
  'cookieParser',
  'favicon',
  'responseTime',
  'errorHandler',
  'timeout',
  'methodOverride',
  'vhost',
  'csrf',
  'directory',
  'limit',
  'multipart',
  'staticCache',
].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
  });
});

这里有一个数组,通过 forEach 遍历它,为 exports 定义这些 name 属性,设置它的 getter 方法,当调用这个name 属性的时候抛出一个错误,告诉开发者,现在 express 里面已经不包含这些模块了,你需要自己去安装。