定义路由的时候发生了什么

还是这个简单例子:

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

app.get('/', function (req, res) {
  res.send('hello world')
})

app.listen(3000)

这其中app.get('/',function(req, res){res.send('hello world')});会处理访问/路径的get请求,到底发生了什么,这就需要去代码里找找看。

app.init()

引入express模块后,执行了var app = express()这一句,其实就是执行了express的主函数:

function createApplication() {
  var app = function(req, res, next) {
    app.handle(req, res, next);
  };
  //新建一个app函数,参数里的req和res是原生的请求和相应对象原型的实例
  //这个app函数作为参数被传进http.createServer里,也就是说app.handle()就是处理所有请求的中间件

  mixin(app, EventEmitter.prototype, false); //将EventEmitter类的原型上的属性都合并入app中
  mixin(app, proto, false); //proto是application.js导出的对象,带有很多属性,将这些属性合并到app上

  // expose the prototype that will get set on requests
  app.request = Object.create(req, {
    app: { configurable: true, enumerable: true, writable: true, value: app }
  })
  //request属性继承自原生请求对象,http.IncomingMessage.prototype

  // expose the prototype that will get set on responses
  app.response = Object.create(res, {
    app: { configurable: true, enumerable: true, writable: true, value: app }
  })
  //response属性继承自原生响应对象,http.ServerResponse.prototype

  //上面的request属性和response属性都多加了一个app的数据属性指向app自己

  app.init(); //app初始化
  
  return app; //返回app函数
}

这其中最后执行了app.init()进行了初始化,app.init()的定义在application.js中,看看做了什么。

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

  this.defaultConfiguration();
};

初始化了几个值,分别是缓存,视图引擎和配置,然后执行了app.defaultConfiguration()。

app.defaultConfiguration = function defaultConfiguration() {
  var env = process.env.NODE_ENV || 'development';
  //获取环境变量中的NODE_ENV,如果没有默认就是development开发环境

  // default settings
  this.enable('x-powered-by');
  //开启x-powered-by响应头,这个响应头会标识后台使用的技术是express框架
  this.set('etag', 'weak');//设置ETag响应头使用弱类型,ETag响应头是资源的特定版本标识符
  this.set('env', env);//设置环境变量
  this.set('query parser', 'extended');
  //设置query中间件为扩展模式,也就是用parseurl解析查询字符串的时候添加一个allowPrototypes: true的选项,使得用户可以修改req.query上的属性
  this.set('subdomain offset', 2);//子域名偏移量
  this.set('trust proxy', false);//是否使用代理来确认客户端的ip地址可靠

  // trust proxy inherit back-compat
  //trustProxyDefaultSymbol,express实例挂载在其他实例上时设置继承trust proxy的标识
  Object.defineProperty(this.settings, trustProxyDefaultSymbol, {
    configurable: true,
    value: true
  });

  debug('booting in %s mode', env);
  //mount事件处理器,当此express实例挂载到另外一个父express实例时,发射mount事件,处理器参数为父实例  
  this.on('mount', function onmount(parent) {
    // inherit trust proxy
    //继承父实例的trust proxy设置
    if (this.settings[trustProxyDefaultSymbol] === true
      && typeof parent.settings['trust proxy fn'] === 'function') {
      delete this.settings['trust proxy'];
      delete this.settings['trust proxy fn'];
    }

    // inherit protos
    //继承父实例的各种设置
    setPrototypeOf(this.request, parent.request)
    setPrototypeOf(this.response, parent.response)
    setPrototypeOf(this.engines, parent.engines)
    setPrototypeOf(this.settings, parent.settings)
  });

  // setup locals
  this.locals = Object.create(null);
  //app.locals初始化是一个没有属性的对象

  // top-most app is mounted at /
  //顶层app挂载在路径/
  this.mountpath = '/';

  // default locals
  this.locals.settings = this.settings;
  //app.locals.settings就是app.settings

  // default configuration
  this.set('view', View);//设置view为定义好的View构造函数
  this.set('views', resolve('views'));//设置默认视图目录
  this.set('jsonp callback name', 'callback');//设置默认jsonp回调名

  if (env === 'production') {
    this.enable('view cache');//生产环境开启视图缓存
  }

  Object.defineProperty(this, 'router', {
    get: function() {
      throw new Error('\'app.router\' is deprecated!\nPlease see the 3.x to 4.x migration guide for details on how to update your app.');
    }
  });
  //为app.router设置访问器属性的get方法,如果调用app.router就警告,因为router已经从app中分离出去了,要单独调用
};

经过这一段执行,app就初始化完成了。

mixin(app, proto, false)

先回到createApplication方法,如果我们在内部打印出app,看看它通过mixin那两步,获得了什么属性,就可以找出是什么时候给他添加上了请求的方法:

function createApplication() {
  var app = function(req, res, next) {
    app.handle(req, res, next);
  };
  console.log(app, 1)
  mixin(app, EventEmitter.prototype, false);
  console.log(app, 2)
  mixin(app, proto, false);
  console.log(app, 3)
  
  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 }
  })
  

  app.init();
  
  return app;
}

分别看一看三次打印,app这个函数获得了什么属性

下面是第一次:

[Function: app] 1

第二次:

{ [Function: app]
  domain: undefined,
  _events: undefined,
  _maxListeners: undefined,
  setMaxListeners: [Function: setMaxListeners],
  getMaxListeners: [Function: getMaxListeners],
  emit: [Function: emit],
  addListener: [Function: addListener],
  on: [Function: addListener],
  prependListener: [Function: prependListener],
  once: [Function: once],
  prependOnceListener: [Function: prependOnceListener],
  removeListener: [Function: removeListener],
  removeAllListeners: [Function: removeAllListeners],
  listeners: [Function: listeners],
  listenerCount: [Function: listenerCount],
  eventNames: [Function: eventNames] } 2

第三次:

{ [Function: app]
  domain: undefined,
  _events: undefined,
  _maxListeners: undefined,
  setMaxListeners: [Function: setMaxListeners],
  getMaxListeners: [Function: getMaxListeners],
  emit: [Function: emit],
  addListener: [Function: addListener],
  on: [Function: addListener],
  prependListener: [Function: prependListener],
  once: [Function: once],
  prependOnceListener: [Function: prependOnceListener],
  removeListener: [Function: removeListener],
  removeAllListeners: [Function: removeAllListeners],
  listeners: [Function: listeners],
  listenerCount: [Function: listenerCount],
  eventNames: [Function: eventNames],
  init: [Function: init],
  defaultConfiguration: [Function: defaultConfiguration],
  lazyrouter: [Function: lazyrouter],
  handle: [Function: handle],
  use: [Function: use],
  route: [Function: route],
  engine: [Function: engine],
  param: [Function: param],
  set: [Function: set],
  path: [Function: path],
  enabled: [Function: enabled],
  disabled: [Function: disabled],
  enable: [Function: enable],
  disable: [Function: disable],
  acl: [Function],
  bind: [Function],
  checkout: [Function],
  connect: [Function],
  copy: [Function],
  delete: [Function],
  get: [Function],
  head: [Function],
  link: [Function],
  lock: [Function],
  'm-search': [Function],
  merge: [Function],
  mkactivity: [Function],
  mkcalendar: [Function],
  mkcol: [Function],
  move: [Function],
  notify: [Function],
  options: [Function],
  patch: [Function],
  post: [Function],
  propfind: [Function],
  proppatch: [Function],
  purge: [Function],
  put: [Function],
  rebind: [Function],
  report: [Function],
  search: [Function],
  subscribe: [Function],
  trace: [Function],
  unbind: [Function],
  unlink: [Function],
  unlock: [Function],
  unsubscribe: [Function],
  all: [Function: all],
  del: [Function],
  render: [Function: render],
  listen: [Function: listen] } 3

对比后可以发现,在经过mixin(app, proto, false);的操作后,app多了这些属性,这些恰恰就是原生http.METHODS:

[ 'acl',
  'bind',
  'checkout',
  'connect',
  'copy',
  'delete',
  'get',
  'head',
  'link',
  'lock',
  'm-search',
  'merge',
  'mkactivity',
  'mkcalendar',
  'mkcol',
  'move',
  'notify',
  'options',
  'patch',
  'post',
  'propfind',
  'proppatch',
  'purge',
  'put',
  'rebind',
  'report',
  'search',
  'subscribe',
  'trace',
  'unbind',
  'unlink',
  'unlock',
  'unsubscribe' ]

所以,这些属性是通过proto这个对象传递过来的,

var proto = require('./application');
所以proto来自application.js文件
app.method()
进入application.js文件,看到上面有一句:
var methods = require('methods'); 

这个第三方模块用于返回友好格式的http.METHODS

然后搜索methods,就能找到如下代码:

/**
 * Delegate `.VERB(...)` calls to `router.VERB(...)`.
 */
//给app添加原生http.METHODS上的方法,在调用的时候将委派给router.VERB

methods.forEach(function(method){
  app[method] = function(path){
    //path参数就是调用时的第一个参数,一般是资源的url

    if (method === 'get' && arguments.length === 1) {
      // app.get(setting)
      return this.set(path);
    }
    //如果类似于这样调用get,app.get(url);,只传递一个参数的话,就把这个参数对应的值找到,然后直接返回这个值
    //app.set()方法用于设置一个值或者获取一个值

    //如果是调用app.get()并且传递两个参数,或者是其他方法例如app.post(),那就走处理请求的流程
    this.lazyrouter(); 
    //lazyrouter(),如果app上还没有router模块,就给app添加上_router属性,_router是router的一个实例
    //lazyrouter()还添加了生成req.query的中间件和初始化中间件,都生成了对应的layer实例,存在了_router.stack里

    //调用router模块上的route方法,传入get请求的url
    var route = this._router.route(path);
    //先创建了一个指定路径的route对象
    //然后使用Route.prototype.dispatch作为fn创建了一个新的layer实例,最后请求会在dispatch那个函数里面处理,新layer实例被加入_router.stack
    //最后返回这个route对象

    route[method].apply(route, slice.call(arguments, 1));
    //为Route对象的指定方法应用第二个参数,即处理函数
    //route.protorype.METHOD又会创建一个layer实例,将当前处理函数作为handle,这个实例最后加入了route.stack里,而不是_router.stack里
    //然后给route实例的methods对象里添入当前对应的http动作,也就是route.methods[method] = true
    return this;
  };
});

这一段为app添加类似get,post请求类型名称的属性时候发生了很多事情,一行一行地分析。

首先如果调用的是app.get()方法的话,而且只传递了一个参数,那就不是为了处理请求,而是为了获取app.set()方法设置的值。app.set()用来设置值,app.get()用来获取app.set()设置的值。app.set()设置的值会存储在app.settings里面。

下面是app.set的源码:

/**
 * Assign `setting` to `val`, or return `setting`'s value.
 *
 *    app.set('foo', 'bar');
 *    app.set('foo');
 *    // => "bar"
 *
 * Mounted servers inherit their parent server's settings.
 *
 * @param {String} setting
 * @param {*} [val]
 * @return {Server} for chaining
 * @public
 */

//app.set()用于设置一个值或者获取一个值

app.set = function set(setting, val) {
  if (arguments.length === 1) {
    // app.get(setting)
    return this.settings[setting];
  }
  //如果只有一个参数,就去获取这个值

  debug('set "%s" to %o', setting, val);
  //使用第三方debug模块打印提示,即将设置一个值

  // set value
  this.settings[setting] = val;
  //将这个值设置在app.settings里,settings这个属性是一个对象,是在app.init()里初始化的

  // trigger matched settings
  //如果是设置特殊值,就触发相应的方法来做相关设置
  
  switch (setting) {
    case 'etag':
      this.set('etag fn', compileETag(val));
      break;
    case 'query parser':
      this.set('query parser fn', compileQueryParser(val));
      break;
    case 'trust proxy':
      this.set('trust proxy fn', compileTrust(val));

      // trust proxy inherit back-compat
      Object.defineProperty(this.settings, trustProxyDefaultSymbol, {
        configurable: true,
        value: false
      });

      break;
  }

  return this;
};

接着下面执行了this.lazyrouter();,这一句为app添加了router模块,还添加了req.query的中间件。

下面是app.lazyrouter:

/**
 * lazily adds the base router if it has not yet been added.
 *
 * We cannot add the base router in the defaultConfiguration because
 * it reads app settings which might be set after that has run.
 *
 * @private
 */

//如果app还没有router模块,就添加上router模块,并为router.stack添加上了解析查询字符串的中间件函数对应的layer实例,还添加了一个初始化的中间件函数
//作者注释:我们不能给defaultConfiguration上直接添加router模块,因为有可能router运行之后,才会去settings对象里设置

app.lazyrouter = function lazyrouter() {
  //判断app上有没有_router属性,如果有就不用添加了
  if (!this._router) {
    //_router属性是一个Router的实例
    this._router = new Router({
      caseSensitive: this.enabled('case sensitive routing'),
      strict: this.enabled('strict routing')
    });
    //enabled用于查看app.settings是否存在某个值,返回布尔值
    //根据app.settings上有没有对应的值来判断是否开启router的caseSensitive和strict
    //caseSensitive,路由的路径path是否大小写敏感
    //strict,path最后面多加了一个反斜杠是否影响路由,例如'/foo'和'/foo/',默认此项关闭,两种被视为同一个路由

    this._router.use(query(this.get('query parser fn')));
    //这里去获取settings里的'query parser fn'这个值,这个值是在app.defaultConfiguration()初始化app的配置的时候加上的
    //app.defaultConfiguration里有一句this.set('query parser', 'extended'),这一句走到app.set()里面其实走了switch语句
    //最终设置了this.set('query parser fn', compileQueryParser(val));,这里的val就是'extended'
    //而compileQueryParser来自文件utils.js,此方法将设置的'query parser'字符串值转换成对应的方法
    //而'extended'对应的方法是parseExtendedQueryString,所以this.set('query parser', 'extended')最终添加的是util.js里的parseExtendedQueryString方法
    //再把这个方法传递给middleware/query.js里的方法,最终返回的query方法用来解析查询字符串,我们常用的req.query就是用这个query方法解析生成的
    //解析查询字符串并生成req.query的中间件函数传给了router.use(),router.use里创建了layer实例来最终负责执行中间件函数,layer被存入_router.stack中

    this._router.use(middleware.init(this));
    //middleware.init()返回了一个初始化的中间件,它的作用是使得req和res可以互相访问对方,并且它们继承原生的对象,还设置了默认的响应头部X-Powered-By
    //接着router.use会生成它的layer实例,然后存入_router.stack中
  }
};

lazyrouter里实例化Router的时候调用的Router构造函数在router/index.js里:

/**
 * Initialize a new `Router` with the given `options`.
 *
 * @param {Object} options
 * @return {Router} which is an callable function
 * @public
 */

 //导出router模块,router模块是一个匿名函数,它return了一个构造函数
var proto = module.exports = function(options) {
  var opts = options || {};

  function router(req, res, next) {
    router.handle(req, res, next);
  }

  // mixin Router class functions
  //设置router函数的原型为proto对象,proto对象在这个文件里的被加入了大量的属性和方法
  //于是router.__proto__ === proto
  //这样router的实例都可以调用添加在proto上的方法
  setPrototypeOf(router, proto)

  //在router函数上初始化一些属性
  router.params = {};
  router._params = [];
  router.caseSensitive = opts.caseSensitive;//caseSensitive路由是否大小写敏感,此项默认关闭,'/Foo'和'/foo'被视为同一个路由
  router.mergeParams = opts.mergeParams;
  router.strict = opts.strict; //strict,path最后面多加了一个反斜杠是否影响路由,例如'/foo'和'/foo/',默认此项关闭,两种被视为同一个路由
  router.stack = []; //stack里存着所有的layer实例,是layer实例最终来负责执行中间件函数

  return router;
  //返回router函数
};

 lazyrouter里面了里面也为app添加了两个中间件函数,一个是query中间件,用来解析请求的query参数,另外是个是一个初始化中间件,这个init中间件初始化了req和res还有res.locals。

下面是query中间件:

/**
 * @param {Object} options
 * @return {Function}
 * @api public
 */

//express已经不包含大部分的请求解析中间件了,如json、urlencoded、cookie等中间件都变成可配置的了,只有查询字符串解析中间件还是内建的
//此处的query函数就是返回一个函数供router模块使用的方法来解析查询字符串,也就是生成req.query

module.exports = function query(options) {
  var opts = merge({}, options)
  var queryparse = qs.parse;
  //使用第三方插件qs作为查询字符串解析器

  if (typeof options === 'function') {
    queryparse = options;
    opts = undefined;
  }
  //如果options参数是一个函数,那么它就作为查询字符串解析器

  if (opts !== undefined && opts.allowPrototypes === undefined) {
    // back-compat for qs module
    opts.allowPrototypes = true;
  }
  //如果options没有allowPrototypes属性,那就给opts上加一个allowPrototypes的属性,这个allowPrototypes是给qs插件传递的参数
  //这个参数为true会允许用户可以随意修改req.query上的属性

  return function query(req, res, next){
    //当req.query不存在的时候,执行解析
    if (!req.query) {
      var val = parseUrl(req).query;
      //使用parseurl插件解析req对象,获取到查询字符串
      req.query = queryparse(val, opts);
      //解析查询字符串为对象,存为req.query
    }

    next();//调用下一个中间件
  };
};

下面是init初始化中间件:

/**
 * Initialization middleware, exposing the
 * request and response to each other, as well
 * as defaulting the X-Powered-By header field.
 *
 * @param {Function} app
 * @return {Function}
 * @api private
 */
//初始化中间件,使得req和res可以互相访问对方,并且它们继承原生的对象,还设置了默认的响应头部X-Powered-By

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)
    //app.request代表原生请求对象http.IncomingMessage.prototype,app.response代表响应对象http.ServerResponse.prototype
    //app.request和app.response是在express.js里的主函数createApplication里定义的

    res.locals = res.locals || Object.create(null);
    //res.locals初始化

    next();
  };
};

为app添加中间件的时候是使用router.use()来添加的, router.use()方法定义在router/index.js里面,去看它内部可以看出添加的每一个中间件处理函数,都生成了一个对应的layer实例,这所有的中间件函数对应的layer实例最后都存入了router.stack这个数组里面。

下面是router.use:

/**
 * Use the given middleware function, with optional path, defaulting to "/".
 *
 * Use (like `.all`) will run for any http METHOD, but it will not add
 * handlers for those methods so OPTIONS requests will not consider `.use`
 * functions even if they could respond.
 *
 * The other difference is that _route_ path is stripped and not visible
 * to the handler function. The main effect of this feature is that mounted
 * handlers can operate without any code changes regardless of the "prefix"
 * pathname.
 *
 * @public
 */
//使用提供好的中间件函数,可选参数是路径,默认路径是/
//use就像all一样会处理任意类型的http动作(例如get,post),但是它不会为这些http动作添加handlers,所以OPTIONS类型的请求不会考虑use方法
//此router.use方法类似于app.use
//中间件就类似一个管道一样,请求从第一个中间件函数处理开始,一直到最后一个,一个一个按顺序通过中间件的栈,对匹配到的路径做处理
//router.use调用的时候最常用方式:
//1.只有一个参数,是一个function
//2.有两个参数,第一个是匹配到的url路径,第二个是一个function
//3.传入一个数组参数,数组里是一个一个中间件function
proto.use = function use(fn) {
  var offset = 0;
  //offset判断参数里第几个参数是function的偏移
  var path = '/';
  //默认匹配到的路径是/
  // default path to '/'
  // disambiguate router.use([fn])
  if (typeof fn !== 'function') {
    var arg = fn;

    while (Array.isArray(arg) && arg.length !== 0) {
      arg = arg[0];
    }
    //此处判断说明router.use也可以传进来一个数组参数,数组里的每一个元素都是一个中间件函数

    // first arg is the path
    if (typeof arg !== 'function') {
      offset = 1;
      path = fn;
    }
    //如果传进来的第一个参数不是function类型,说明第一个参数是path,偏移量为1
  }

  var callbacks = flatten(slice.call(arguments, offset));
  //var slice = Array.prototype.slice;
  //使用slice将函数的参数变成一个数组,然后就可以当做数组来操作了
  //所以Array.prototype.slice.call(arguments, offset)正好返回了offset偏移对应的参数,也就是中间件function,但此时它还是一个数组的形式,接着借用flatten第三方模块将数组扁平化,以防数组出现嵌套,最终的callbacks就是个中间件数组

  //值得一提的是js草案里已经有flatten方法了,但是仍是实验性方法

  if (callbacks.length === 0) {
    throw new TypeError('Router.use() requires a middleware function')
  }
  //如果callbacks数组长度为0,就报错误

  for (var i = 0; i < callbacks.length; i++) {//开始循环callbacks数组
    var fn = callbacks[i];

    if (typeof fn !== 'function') {
      throw new TypeError('Router.use() requires a middleware function but got a ' + gettype(fn))
    }
    //如果不是一个function就报错

    // add the middleware
    debug('use %o %s', path, fn.name || '<anonymous>')
    //输出调试信息

     //使用指定路径、中间件函数创建Layer的实例,继承了router对象的大小写敏感、严格等选项
    var layer = new Layer(path, {
      sensitive: this.caseSensitive,
      strict: false,
      end: false
    }, fn);
    //这个layer实例没有route,说明他不是路由,只是中间件
    layer.route = undefined;
    //将layer实例添加到router的堆
    this.stack.push(layer);
    //也就是说这里有几个中间件函数,就创建几个layer实例,让后把所有layer实例都存入router.stack里面
  }

  return this;
};

 

也就是说中间件处理函数最后放在了layer.handle上。

到这里this.lazyrouter()就分析完了。

接着看var route = this._router.route(path);调用了router的route方法,它根据给定的path路径创建新的route实例。并且创建了对应的layer实例,这个layer是带有route属性的,说明此中间件是带路由的。

下面是router.route:

/**
 * Create a new Route for the given path.
 *
 * Each route contains a separate middleware stack and VERB handlers.
 *
 * See the Route api documentation for details on adding handlers
 * and middleware to routes.
 *
 * @param {String} path
 * @return {Route}
 * @public
 */

//对给定路径创建新route实例
//每个route实例会包含一个分离的中间件stack和http动作handlers
proto.route = function route(path) {
  var route = new Route(path);//创建一个新route实例

  var layer = new Layer(path, {
    sensitive: this.caseSensitive,
    strict: this.strict,
    end: true
  }, route.dispatch.bind(route));
  //route.dispatch.bind(route),把Route.prototype.dispatch的this设置为当前route实例,创建一个新dispatch函数作为参数来创建layer实例
  //Route.prototype.dispatch用于分配req和res对象
  //这个layer的handle就是dispatch方法
  //请求会最终在这个dispatch里面处理

  layer.route = route;
  //这个Layer有route属性,代表了它是一个路由层,而不是中间件层
  this.stack.push(layer);//_router.stack加入新layer
  return route;//返回新route实例
};

接着最后执行了route[method].apply(route, slice.call(arguments, 1));

它将this指向设置成当前route实例,调用已经在route里面定义好的http 动作函数,传入的参数就是处理函数,route里会根据这个处理函数生成对应的layer,这个layer会存入route.stack里面。

下面是在route.js里为route.prototype定义好的http 动作函数:

//循环http动作给Route.prototype添加上统一的处理函数,调用时直接用apply在设置了path的route实例上调用,传进来的参数是处理函数
methods.forEach(function(method){
  Route.prototype[method] = function(){
    var handles = flatten(slice.call(arguments));//将处理函数参数转换成一个数组

    for (var i = 0; i < handles.length; i++) {//循环处理函数数组
      var handle = handles[i];

      if (typeof handle !== 'function') {
        var type = toString.call(handle);
        var msg = 'Route.' + method + '() requires a callback function but got a ' + type
        throw new Error(msg);
      }//如果处理函数不是function类型,就报错

      debug('%s %o', method, this.path)

      var layer = Layer('/', {}, handle);//以当前的处理函数新建一个layer实例
      layer.method = method;//layer.method为当前的http method

      this.methods[method] = true;//给当前apply调用的route实例的methods属性里加入当前http method
      this.stack.push(layer);//当前route实例的stack中加入layer实例
    }
    //可以看到这里生成的layer实例没有设置path,而是设置成'/',这是因为path在route实例里面设置过了
    return this;
  };
});

通过上面的分析发现,不管是什么样的处理函数最终都是一个layer实例,处理所有请求的中间件例如query和 init所对应的layer都存在了router实例的stack了里面。

而用户自定义的带路由路径的处理函数所对应的layer实例都存在了对应route实例的stack里面。而这个route实例是一个路由层layer的属性,这个layer也在router.stack里面。

也就是说app._router.stack里面有两种layer,一种是query和init这种,另外一种是指定路径路由layer,路径layer有一个route属性,route上面还有个route.stack里面存储着最终处理函数的那个layer。

app.listen()

app.listen = function listen() {
  var server = http.createServer(this); //将app传递给http.createServer创建服务器实例
  return server.listen.apply(server, arguments); //监听端口
};

 

posted @ 2018-04-14 01:32  hahazexia  阅读(421)  评论(0)    收藏  举报