Contents of this series

1. GlobalI went to see

2. Routes are matchedI went to see

3. Error handlingI went to see

4. View Templates (this article)

5. Design ideaI went to see


1. About the View template engine

When you first hear the word engine, it sounds like something fancy, but it’s simply a tool that injects data into static templates.

(Picture source network, such as infringement contact, delete immediately)

The main battlefield of javascript template engine is browser and Node side, the two ends are actually similar, commonly used is Jade, EJS, art-template,

While Art-Template is known as the invincible super Thunderwhirlwind fast, have time to study their specific implementation of the similarities and differences, no matter now.

2. Express and View template engine

The template engine has been around for a long time.

2.1 Preparation Phase

Most of the initial Settings for a view template are in the app.Settings object, which has properties like

  • View manages the class of view templates, with each instance corresponding to a trial template file

  • Path for storing view template files

  • View Engine Specifies the default view template engine name

    There is also a property about the engine type of the view template, which is placed separately in the app. Engines object

2.2 Using View Templates

First set up the View, view Engine Settings

app.set('views', path.join(__dirname, 'views'));
app.set('view engine'.'jade');
Copy the code

Then call res.render(‘ template file name ‘) to render directly, such as render and return the error page

// error handler
app.use(function (err, req, res, next) {
  // set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = req.app.get('env') = = ='development' ? err : {};
  // render the error page
  res.status(err.status || 500);
  res.render('error');
});
Copy the code

There is also a less commonly used interface exposed to app objects

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

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

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

  return this;
};
Copy the code

Its main function is to set how the view template file corresponding to the extension should be rendered. At the end of this article, we will write a simple view template engine and use this interface to define the rendering operation.

2.3 Detailed Process

View templates are the primary tool for generating static HTML files, and the only interface exposed on Express is res.render, so we’ll start with this function

res.render = function render(view, options, callback) { //codes... }
Copy the code

As you can see, the view parameter is mandatory, which is the name of the template file to render. The remaining two parameters are optional, which are internally wrapped according to the number of parameters. Opts combines the options and the locals properties of the response object. The done argument is passed in, optionally, as a function that receives the last string output from the template, otherwise it would simply be wrapped as follows

 done = done || function (err, str) {
    if (err) return req.next(err);
    self.send(str);
 };
Copy the code

App.render is called and the new three arguments are passed in

// render
app.render(view, opts, done);
Copy the code

In app.render, opTS options are merged again, incorporating some global attributes, and finally generating a renderOptions object that represents all options for a template render. It also generates a template file management instance, which is an instance of the View mentioned earlier. This instance manages the mapping between the template file of this name and the corresponding parsing engine. If this management instance is cached, it will be fetched directly from the cache. Finally pass the view instance, the renderOptions object, and the wrapped Done function to the tryRender function

// render
tryRender(view, renderOptions, done);
Copy the code

The tryRender function is just error catching, internally calling View,render, and passing renderOptions and done

function tryRender(view, options, callback) {
  try {
    view.render(options, callback);
  } catch(err) { callback(err); }}Copy the code

The render method is used to render the view template file. The render method is used to render the view template file

View.prototype.render = function render(options, callback) {
  debug('render "%s"'.this.path);
  this.engine(this.path, options, callback);
};
Copy the code

In the rendering logic above, there is the engine method, one per view instance, which defines the actual rendering process as a function that renders the file passed in to path, and as a constructor

// Get the extension name of the view template file
this.ext = extname(name);
if (!this.ext) {
    // get extension from default engine name
    this.ext = this.defaultEngine[0]! = ='. '
      ? '. ' + this.defaultEngine
      : this.defaultEngine
    fileName += this.ext;
  }
// If there is no rendering engine for the extension, require it
  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 (typeoffn ! = ='function') {
      throw new Error('Module "' + mod + '" does not provide a view engine.')
    }
    opts.engines[this.ext] = fn
  }
  // store loaded engine
  this.engine = opts.engines[this.ext];
Copy the code

If you can’t find it, require it. This requires that the template engine implements the __Express interface, which can read the template file and parse it into a string HTML. This is passed as a parameter to the engine function, and finally sent to the browser through the callback method.

The whole detailed process can be summarized as the following flow chart:

3. Customize simple view template engine and adapt Express

Because it is easy to write, I will not write NPM package, directly use the app. Engine interface to connect to Express

3.1 Create a template file named VIN and write simple characters

// hello.vin
<html>
<head>
    <title>$title$</title>
</head>
input content is $content$;
</html>
Copy the code

3.2 Define the transformation function, which is the core function of the engine

const fs = require('fs');
const regexp = / (? < = RMB). *? (? = RMB)/g;
module.exports = function VinParser(path, options, callback) {
    var res;
    try {
        str = fs.readFileSync(path).toString();
        const matchedKeys = str.match(regexp);
        for (let key of matchedKeys) {
            console.log(key, options[key]);
            str = str.replace(` RMB${key}RMB `, options[key]);
        }
        res = str;
    } catch (err) {
        callback(err);
    }
    callback(null, res);
}
Copy the code

(Don’t scold don’t scold, I re really don’t, just look good)

3.3 Call the app.engine interface to define the file extension named. Vin parsing engine is the above function

const VinParser = require('./VinParser.js');
app.engine('vin', VinParser);
Copy the code

3.4 🐋 is available

/* GET home page. */
router.get('/'.function (req, res, next) {
  res.render('hello', {
    content: 'HelloWorld'.title: 'vin template'
  });
});
Copy the code

Results the following

No summary of this article

The 🦧