Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

expressjs-sourceCode-application.js/express.js #123

Open
yaofly2012 opened this issue Mar 18, 2020 · 2 comments
Open

expressjs-sourceCode-application.js/express.js #123

yaofly2012 opened this issue Mar 18, 2020 · 2 comments

Comments

@yaofly2012
Copy link
Owner

yaofly2012 commented Mar 18, 2020

express.js模块

express的入口,暴露顶层的API和类型。

1. createApplication函数

express对外暴露的最顶层对象(也是函数)

exports = module.exports = createApplication;
  1. 这里有个比较好的习惯就是时刻保持exportsmodule.exports的一致性,即使全局只导出一个变量(如Express里只导出createApplication函数)。

2. createApplication函数逻辑

用来创建express aplication的。

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;
}
  1. app是个函数,只不过挂载了一些“继承”的属性和方法;
  1. app采用mixin方式继承application模块和 EventEmitter.prototype的属性和方法。

2.2 Issues

  1. 还不清楚app.request/response的用处 ?
@yaofly2012
Copy link
Owner Author

yaofly2012 commented Mar 18, 2020

application.js模块

属性:

  1. this.parent
  2. this._router
  3. this.settings

1. application.listen 方法

app.listen = function listen() {
  var server = http.createServer(this);
  // 通过`apply`方式调用http.server方法为方便透传实参
  return server.listen.apply(server, arguments);
}
  1. 本质上app就是个requestListener
  2. 还可以单独的创建Http服务,然后监听:
var http = require('http')
       , https = require('https')
       , express = require('express')
       , app = express();
 
     http.createServer(app).listen(80);
     https.createServer({}, app).listen(443);

2. this._router(Base/Root router)相关方法

2.1 lazyrouter创建this._router方法

app.lazyrouter = function lazyrouter() {
  if (!this._router) {
    this._router = new Router({
      caseSensitive: this.enabled('case sensitive routing'),
      strict: this.enabled('strict routing')
    });

    this._router.use(query(this.get('query parser fn')));
    this._router.use(middleware.init(this));
  }
}
  1. this._router默认会挂载两个中间件queryinit
exports.init = function(app){
  return function expressInit(req, res, next){
    if (app.enabled('x-powered-by')) res.setHeader('X-Powered-By', 'Express');
    req.res = res;
    res.req = req;
    req.next = next;

    setPrototypeOf(req, app.request)
    setPrototypeOf(res, app.response)

    res.locals = res.locals || Object.create(null);

    next();
  };
};
  • reqres为啥要互相引用?
  • req也挂载了next方法
  • res.locals表达了什么意图?

跨中间件变量传递方式?

  1. 利用req/res对象本身
  2. res.locals
  3. app.locals

思考:
中间件的用途是为了增强reqres功能。
handler则主要用于处理请求产生响应内容。

其他:

  1. use -> Router#use()
  2. route -> Router#route()
  3. param -> Router#param()
  4. app.METHOD -> Router#METHOD()
    app.get的双重含义
  5. all =>

3. 配置相关方法

3.1 set/get

3.2 针对开关式(boolean)配置提供简便两组方法

  1. enable/enabled
  2. disable/disabled

3.3 Issues

  1. 有些配置名称都是以“句子”方法命名,为啥啊?

@yaofly2012 yaofly2012 changed the title expressjs-sourceCode-application expressjs-sourceCode-application.js/express.js Mar 21, 2020
@yaofly2012
Copy link
Owner Author

yaofly2012 commented Mar 26, 2020

app.use

既可以挂载中间件(作为Router#use代理),也可以挂载其他app。

app.use = function use(fn) {
  var offset = 0;
  var path = '/';

  // default path to '/'
  // disambiguate app.use([fn])
  if (typeof fn !== 'function') {
    var arg = fn;
   // 递归的遍历型如`[[fn]]`入参
    while (Array.isArray(arg) && arg.length !== 0) {
      arg = arg[0];
    }

    // first arg is the path
    if (typeof arg !== 'function') {
      offset = 1;
      path = fn;
    }
  }
  // 中间件函数列表
  var fns = flatten(slice.call(arguments, offset));

  if (fns.length === 0) {
    throw new TypeError('app.use() requires a middleware function')
  }

  // setup router
  this.lazyrouter();
  var router = this._router;

  fns.forEach(function (fn) {
    // non-express app 判断中间件函数是否为app的方式
    if (!fn || !fn.handle || !fn.set) {
      return router.use(path, fn);
    }
    // 挂载的是个app(子app)
    debug('.use app under %s', path);
    fn.mountpath = path;
    fn.parent = this;

    // restore .app property on req and res
    router.use(path, function mounted_app(req, res, next) {
    //  `createApplication`方法里向req挂载app在这里用到了
    var orig = req.app;

      fn.handle(req, res, function (err) {
        // 子app执行完后,恢复父app的数据
        setPrototypeOf(req, orig.request)
        setPrototypeOf(res, orig.response)
        next(err);
      });
    });

    // mounted an app
    fn.emit('mount', this);
  }, this);

  return this;
}

@yaofly2012 yaofly2012 removed the NodeJS label Jan 13, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant