nodejs express 框架解密5-檢視

yupeng發表於2013-12-19

 本文件是基於express 3.4.6 的

在我們的程式碼中,渲染模板大致是這樣寫的

exports.index = function(req, res){
  res.render('index', { title: 'Express' });
};

這個req,res 函式其實是經過了中介軟體middleware.js 處理後的,我們在前面提到過。

req,res的原型分別為 app.request 和app.response

req.__proto__ = app.request;
res.__proto__ = app.response;

而 app.request 和app.response,本身也是具有app的屬性的。

//設定app 的request物件的原型為req,本身的屬性為connect物件
app.request = { __proto__: req, app: app };
//設定app的response物件原型為res ,本身的屬性為connect物件
app.response = { __proto__: res, app: app };

注意這裡的這個app屬性

開啟response.js 檔案,我們看看res.render方法

res.render = function(view, options, fn){
  var self = this
    , options = options || {}
    , req = this.req
    , app = req.app;

  // support callback function as second arg
  if ('function' == typeof options) {
    fn = options, options = {};
  }

  // merge res.locals
  options._locals = self.locals;

  // default callback to respond
  fn = fn || function(err, str){
    if (err) return req.next(err);
    self.send(str);
  };

  // render
  app.render(view, options, fn);
};

可以看到:

, req = this.req
, app = req.app;

這裡的 req = this.req,其實是中介軟體 裡面的  res.req = req;

第二句直接將app帶進來了,最後我們執行了app.render方法,它呼叫了application.js 中得這個方法:

app.render = function(name, options, fn){
  var opts = {}
    , cache = this.cache
    , engines = this.engines
    , view;

  // support callback function as second arg
  if ('function' == typeof options) {
    fn = options, options = {};
  }

  // merge app.locals
  utils.merge(opts, this.locals);

  // merge options._locals
  if (options._locals) utils.merge(opts, options._locals);

  // merge options
  utils.merge(opts, options);

  // set .cache unless explicitly provided
  opts.cache = null == opts.cache
    ? this.enabled('view cache')
    : opts.cache;

  // primed cache
  if (opts.cache) view = cache[name];

  // view
  if (!view) {
    view = new (this.get('view'))(name, {
      defaultEngine: this.get('view engine'),
      root: this.get('views'),
      engines: engines
    });

    if (!view.path) {
      var err = new Error('Failed to lookup view "' + name + '"');
      err.view = view;
      return fn(err);
    }

    // prime the cache
    if (opts.cache) cache[name] = view;
  }

  // render
  try {
    view.render(opts, fn);
  } catch (err) {
    fn(err);
  }
};

可以看到它呼叫了檢視類:

 view = new (this.get('view'))(name, {
      defaultEngine: this.get('view engine'),
      root: this.get('views'),
      engines: engines
    });

最後呼叫了檢視的render方法。 view.render(opts, fn);

檢視的這個方法是:

View.prototype.render = function(options, fn){
  this.engine(this.path, options, fn);
};
function View(name, options) {
  options = options || {};
  this.name = name;
  this.root = options.root;
  var engines = options.engines;
  this.defaultEngine = options.defaultEngine;
  var ext = this.ext = extname(name);
  if (!ext && !this.defaultEngine) throw new Error('No default engine was specified and no extension was provided.');
  if (!ext) name += (ext = this.ext = ('.' != this.defaultEngine[0] ? '.' : '') + this.defaultEngine);
  this.engine = engines[ext] || (engines[ext] = require(ext.slice(1)).__express);
  this.path = this.lookup(name);
}

引擎require了一個方法

this.engine = engines[ext] || (engines[ext] = require(ext.slice(1)).__express);

後面就去呼叫具體的模板引擎了。

相關文章