模板引擎允许您在Express中使用静态模板文件。在运行时,模板引擎用实际值替换模板文件中的变量,并将模板转换为HTML文件发送到客户端。这种方法使得设计HTML页面更容易。

常见的模板引擎有:

设置模板引擎

Express可以通过如下方式来这是应用的模板引擎

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');

以上模板引擎的设置,会根据设置的'view engine'值,自动引入模板引擎的NPM包,所以在此之前必须要安装pug

npm install pug --save

它等价于以下写法:

const pug = require('pug');

app.set('views', path.join(__dirname, 'views'));
app.engine('pug', pug.__express);

源码解析

模板引擎的关键代码主要在两个文件中:express/lib/application.jsexpress/lib/view.js

express/lib/application.js

app.engine = function engine(ext, fn) {
  if (typeof fn !== 'function') {
    throw new Error('callback function required');
  }

  // get file extension
  var extension = ext[0] !== '.'
    ? '.' + ext
    : ext;

  // store engine
  this.engines[extension] = fn;

  return this;
};

app.render = function render(name, options, callback) {
  ...
  // view
  if (!view) {
    var View = this.get('view');

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

    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.view = view;
      return done(err);
    }

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

express/lib/view.js

function View(name, options) {
  ...
  if (!opts.engines[this.ext]) {
    // load engine
    var mod = this.ext.substr(1)
    debug('require "%s"', mod)

    // default engine export
    var fn = require(mod).__express

    if (typeof fn !== 'function') {
      throw new Error('Module "' + mod + '" does not provide a view engine.')
    }

    opts.engines[this.ext] = fn
  }
  ...
}

开发模板引擎

由上所知,我们有两种方式来开发Express的模板引擎:

  • 通过app.set('view engine', engineName)方法,自动引入方式
  • 通过app.engine(ext, fn)方法,注册模板引擎方式

这两种方法,本质上是一致的,暂只介绍第二种方式

// engineExt: 模板文件扩展名
app.engine(engineExt, function(filepath, options, callback){
  // filepath: 模板文件的路径
  // options: 渲染模板所用的参数
  // callback: 渲染完成回调
	
  // 参数一:渲染过程的错误,如成功,则为null
  // 参数二:渲染出来的字符串
  return callback(null, 'Hello World');
});

举个例子:

app.set('views', './views');
app.engine('tmpl', function(filepath, options, callback){
  return callback(null, 'Hello World');
});
// 通过源码知道,此处并不需要
app.set('view engine', 'tmpl');

// 转换为HTML到客户端
app.get('/', function (req, res) {
  res.render('index', { title: 'Hello World'})
});

混合使用多种模板引擎

在render方法上只要带上文件扩展名,Express就会根据扩展名加载相应的模板引擎。

app.get('/index.jade', function (req, res, next) {
  res.render('index.jade', {title: 'jade'});
});

app.get('/index.ejs', function (req, res, next) {
  res.render('index.ejs', {title: 'ejs'});
});

参考