express之application.js
发布日期:2021-05-28 16:23:54 浏览次数:10 分类:技术文章

本文共 10293 字,大约阅读时间需要 34 分钟。

lib/application.js//express应用程序原型var app = exports = module.exports = {};var trustProxyDefaultSymbol = '@@symbol:trust_proxy_default';//express实例初始化方法,使用express()创建express实例时会调用此方法app.init = function init() {  //视图缓存?  this.cache = {};  //模板引擎对象,键为模板扩展名,值为引擎构造函数  this.engines = {};  //express实例上的配置  this.settings = {};  //默认配置方法,创建express实例后的配置会覆盖默认配置  this.defaultConfiguration();};//默认配置方法app.defaultConfiguration = function defaultConfiguration() {  //获取环境,默认为开发环境  var env = process.env.NODE_ENV || 'development';  // default settings  this.enable('x-powered-by');  this.set('etag', 'weak');  //设置环境  this.set('env', env);  this.set('query parser', 'extended');  //子域偏移  this.set('subdomain offset', 2);  //信任代理  this.set('trust proxy', false);  // trust proxy inherit back-compat  Object.defineProperty(this.settings, trustProxyDefaultSymbol, {    configurable: true,    value: true  });  debug('booting in %s mode', env);  //express实例本身是事件发射器,在express()方法中继承事件发射器  //mount事件处理器,当此express实例挂载到另外一个父express实例时,发射mount事件,处理器参数为父实例  this.on('mount', function onmount(parent) {    //根据父实例设置...    if (this.settings[trustProxyDefaultSymbol] === true &&      typeof parent.settings['trust proxy fn'] === 'function') {      delete this.settings['trust proxy'];      delete this.settings['trust proxy fn'];    }    // 继承父实例的各种原型    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);  //顶层挂载路径  this.mountpath = '/';  // default locals  this.locals.settings = this.settings;  //默认配置,视图类、视图所在目录、jsonp回调  this.set('view', View);  this.set('views', resolve('views'));  this.set('jsonp callback name', 'callback');  //生产环境,启用视图缓存  if (env === 'production') {    this.enable('view cache');  }  //router属性已经被废弃  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.');    }  });};//延迟初始化_router属性,为一个Router实例//必须延迟初始化,因为需要读取后续配置,后续配置可能覆盖默认配置,所以不能再init方法中初始化app.lazyrouter = function lazyrouter() {  //如果_router属性不存在,创建一个Router实例  if (!this._router) {    this._router = new Router({      //路由区分大小写设置与express实例一致      caseSensitive: this.enabled('case sensitive routing'),      //严格路由设置与express实例一致      strict: this.enabled('strict routing')    });    //应用默认的全局中间件,未设置路径即挂载到/路径    this._router.use(query(this.get('query parser fn')));    this._router.use(middleware.init(this));  }};//应用程序的请求处理方法,第一个参数为node原生req对象,第二个参数为node原生res对象app.handle = function handle(req, res, callback) {  //获取根Router  var router = this._router;  //最后的处理器,如果参数未设置,采用默认处理器  var done = callback || finalhandler(req, res, {    env: this.get('env'),    onerror: logerror.bind(this)  });  //根Router对象不存在  if (!router) {    debug('no routes defined on app');    //调用最后处理器    done();    return;  }  //使用根Router对象处理请求  router.handle(req, res, done);};//添加中间件方法,如果只有中间件函数,则挂载到'/'路径//内部还是将中间件挂载到根Router上//可以一次对一个path挂载多个中间件函数app.use = function use(fn) {  //函数参数偏移  var offset = 0;  //默认挂载路径为根路径  var path = '/';  //第一个参数不是函数  if (typeof fn !== 'function') {    var arg = fn;    //参数为数组,递归获取它的第一个元素    while (Array.isArray(arg) && arg.length !== 0) {      arg = arg[0];    }    //如果参数不是数组也不是函数,则它为路径    if (typeof arg !== 'function') {      //此时函数偏移为1      offset = 1;      path = fn;    }  }  //获取中间件数组  var fns = flatten(slice.call(arguments, offset));  //函数不存在,抛出错误  if (fns.length === 0) {    throw new TypeError('app.use() requires a middleware function')  }  //延迟初始化根Router对象,即说明调用use时所有配置已经完成,后续配置不起作用  this.lazyrouter();  //获取根Router  var router = this._router;  //遍历中间件函数数组  fns.forEach(function (fn) {    //中间件函数不存在    //或者存在且没有handle方法,说明是普通中间件函数    //获取存在且有handle方法且没有set方法,说明是Router对象    if (!fn || !fn.handle || !fn.set) {      //直接将中间件函数设置到根Router对象上      return router.use(path, fn);    }    //当中间件函数存在,且有handle属性,且有set属性时    //说明中间件函数本身是一个express应用程序实例    debug('.use app under %s', path);    //设置express实例挂载路径    fn.mountpath = path;    //设置父实例    fn.parent = this;    //在根Router对象上挂载app处理函数    router.use(path, function mounted_app(req, res, next) {      //获取父实例      var orig = req.app;      //使用子实例的handle函数来处理      fn.handle(req, res, function (err) {        //为什么遇到错误时要设置原型?        setPrototypeOf(req, orig.request)        setPrototypeOf(res, orig.response)        next(err);      });    });    //向子实例发射mount事件,将父实例传递给事件处理器    fn.emit('mount', this);  }, this);  return this;};//代理了根Router对象的route方法,返回一个Route对象,一个Route对象代表一个封闭的中间件栈app.route = function route(path) {  //延迟初始化根Router对象  this.lazyrouter();  //调用根Router对象的route方法  return this._router.route(path);};//注册模板引擎extapp.engine = function engine(ext, fn) {  //第二个参数必须为回调函数  if (typeof fn !== 'function') {    throw new Error('callback function required');  }  //获取扩展名,如果模板引擎不是以.开头,加上.  var extension = ext[0] !== '.' ?'.' + ext :ext;  //设置到engines对象,键为扩展名,值为函数,其实是模板引擎构造函数  //说明可以设置多个模板引擎  this.engines[extension] = fn;  return this;};//代理了根Router对象的param方法,name可以是数组app.param = function param(name, fn) {  this.lazyrouter();  if (Array.isArray(name)) {    for (var i = 0; i < name.length; i++) {      this.param(name[i], fn);    }    return this;  }  this._router.param(name, fn);  return this;};//set与get方法合体,两个参数为set,一个参数为getapp.set = function set(setting, val) {  //一个参数时返回值  if (arguments.length === 1) {    return this.settings[setting];  }  debug('set "%s" to %o', setting, val);  //两个参数时设置值  this.settings[setting] = val;  //对指定键改变键名  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;};//返回此express实例的绝对路径app.path = function path() {  //如果有父实例,为父实例路径加上此实例挂载路径  //如果为根实例,为''  return this.parent ?this.parent.path() + this.mountpath :'';};//指定设置值是否启用app.enabled = function enabled(setting) {  return Boolean(this.set(setting));};//指定设置值是否禁用app.disabled = function disabled(setting) {  return !this.set(setting);};//启用设置,其值为trueapp.enable = function enable(setting) {  return this.set(setting, true);};//禁用设置,其值为falseapp.disable = function disable(setting) {  return this.set(setting, false);};//将所有http方法名遍历设置为express实例函数属性methods.forEach(function (method) {  //方法名为属性名,值为函数,函数用来挂载中间件函数到指定路径  app[method] = function (path) {    //如果是get方法,且函数只有一个参数,说明是获取指定设置值,这是个例外    if (method === 'get' && arguments.length === 1) {      //返回指定设置值      return this.set(path);    }    //延迟初始化根Router对象    this.lazyrouter();    //获取指定路径Route对象    var route = this._router.route(path);    //为Route对象的指定方法应用第二个参数,即中间件函数    route[method].apply(route, slice.call(arguments, 1));    return this;  };});//为所有http方法,挂载中间件函数到指定路径app.all = function all(path) {  //初始化根Router对象  this.lazyrouter();  //获取指定路径Route对象  var route = this._router.route(path);  //获取中间件函数,可以为多个  var args = slice.call(arguments, 1);  //遍历所有http方法  for (var i = 0; i < methods.length; i++) {    //将中间件函数设置到指定方法上    route[methods[i]].apply(route, args);  }  return this;};//已废弃app.del = deprecate.function(app.delete, 'app.del: Use app.delete instead');//渲染指定名称模板//name:模板名称,不包含扩展名//options:传递给模板的属性对象//callback:回调函数,接收渲染错误,与渲染出的字符串app.render = function render(name, options, callback) {  //视图缓存  var cache = this.cache;  var done = callback;  //模板引擎  var engines = this.engines;  var opts = options;  var renderOptions = {};  var view;  // 如果第二个参数为函数,说明属性不存在  if (typeof options === 'function') {    done = options;    opts = {};  }  //渲染选项,合并本地设置  merge(renderOptions, this.locals);  //再合并选项中_locals  if (opts._locals) {    merge(renderOptions, opts._locals);  }  //再合并opts  merge(renderOptions, opts);  // 如果渲染时没有指定cache,则使用全局配置的view cache  if (renderOptions.cache == null) {    renderOptions.cache = this.enabled('view cache');  }  //如果启用了cache  if (renderOptions.cache) {    //从缓存中获取视图对象    view = cache[name];  }  //如果视图不存在  if (!view) {    //获取设置中视图类    var View = this.get('view');    //创建视图对象,指定视图名    view = new View(name, {      //默认视图引擎      defaultEngine: this.get('view engine'),      //视图目录      root: this.get('views'),      //引擎存储      engines: engines    });    //如果视图对象path属性不存在,抛出错误到回调函数    if (!view.path) {      //获取视图目录信息      var dirs = Array.isArray(view.root) && view.root.length > 1 ?        'directories "' + view.root.slice(0, -1).join('", "') + '" or "' + view.root[view.root.length - 1] + '"' :        'directory "' + view.root + '"'      var err = new Error('Failed to lookup view "' + name + '" in views ' + dirs);      //设置视图对象到err对象      err.view = view;      return done(err);    }    //正常情况,按照设置缓存视图    if (renderOptions.cache) {      cache[name] = view;    }  }  //尝试渲染视图对象  tryRender(view, renderOptions, done);};//express实例启动监听app.listen = function listen() {  //node原生http服务器实例,因为express实例本身就是一个(req,res,next)类型函数,所以可以作为处理函数传递给方法  var server = http.createServer(this);  //使用server实例启动监听,将端口等参数传递给它  return server.listen.apply(server, arguments);};//错误日志function logerror(err) {  //test环境不输出错误  if (this.get('env') !== 'test') console.error(err.stack || err.toString());}//尝试渲染指定视图对象function tryRender(view, options, callback) {  try {    //视图对象的render方法    view.render(options, callback);  } catch (err) {    callback(err);  }}

 

app其实只是express()方法得到实例的一个原型对象,其被express实例所继承,还有其他一些内容在express()方法内设置,从源码可以得出:

1.express实例创建时,会调用init方法,进行初始化配置

2.一个express实例可以使用use方法挂载到另外一个express实例上去,会触发它的mount事件

3.每个express实例都有一个根Router对象,用来设置、处理路由,而且这个对象都是延迟到use、route、get、post...等方法调用时才会初始化,在这之前还可以配置express实例,以覆盖默认配置,app的handle方法内部还是调用了_router属性的handle方法

4.每个根Router对象都会添加两个全局中间件

5.使用use方法挂载中间件到根Router对象上时,可以挂载三种对象:普通中间件、Router对象、express实例,其中express实例需要包装后再挂载,挂载时触发mount事件

6.可以为不同扩展名模板设置不同视图引擎

7.app的set方法有两用,一个参数与两个参数效果不同,get方法也存在

8.挂载中间件有两个模式:一个模式时use中直接挂载到根Router对象,另外一个模式是get、post、all...等方法中先获取指定路径route(注意与Router区分)对象,在将中间件挂载到它的指定http方法上

9.渲染视图时会生成View对象,内部调用view的render'方法渲染,可以在render方法中指定缓存选项来覆盖全局缓存,缓存中存储得是name与View对象的映射

 

 

 

转载地址:https://blog.csdn.net/qq_27868061/article/details/79245906 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:express之express.js
下一篇:nestjs中间件之MiddlewareBuilder类

发表评论

最新留言

路过按个爪印,很不错,赞一个!
[***.219.124.196]2024年02月14日 15时54分48秒