Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to render precompiled Jade (Pug) templates in a Node.js Express app?

I have a nodejs app that uses Express and Jade(now Pug). I want to precompile the jade templates for better performance. I have been able to convert the jade to javascript using jade --client to compile all the .jade files to corresponding .js.

How do I use these js files in my app? I don't have much experience with nodejs/express, but I think it must require changing the rendering engine? I have read the official documentation that says to use runtime.js but I couldn't find any documentation as how exactly it is to be used.

like image 893
apoorvumang Avatar asked Apr 24 '13 04:04

apoorvumang


2 Answers

Jade (now Pug) is a great templating engine, but the compilation of the views consume so much resources. Jade is slow. Of course you can use the "cache view" feature of express on production that caches the compiled views, but the compiled views are stored in memory that can consume so much resources. Thanks to Jade, the latest express app that I had developed responded to the first request in more than 800ms, so as I didn't want to change the templating engine, I, just like you, decided to compile the Jade views on my development environment and deploy these files instead of original views.

In order to use the compiled views which are .js files consisting of a function named template, you need to use the jade runtime. The runtime generates the attribute values and does other stuff like escaping values. The template function accepts one argument which is for dynamic data.

Since these functions are compiled for client side, the compiled views depends on a global jade runtime (i.e. window.jade). In node.js you can populate the GLOBAL object, i.e. GLOBAL.jade = require('jade/lib/runtime') but it's not a good idea. I decided to modify the compiled function so:

  1. We can require the compiled view (.js files) by using module.exports.
  2. The function accepts a second argument which is the jade runtime.

The following code snippet uses gulp, gulp-jade and gulp-replace node modules in a .gulpfile:

gulp.task('compile-jade', () => {
    // get all the jade files and compile them for client
    return gulp.src([
        '../views/**/*.jade'
    ]).pipe(jade({
        client: true
    }))
    // replace the function definition
    .pipe(replace('function template(locals)', 'module.exports = function(locals, jade)'))
    .pipe(gulp.dest('../views_js') );
});

Now that we have changed the function declaration we can load the compiled files (.js files) instead of the .jade files. But there is another problem. There is no pre-made templating engine (as far as I know) for using these files. So we need to define a new engine. It's pretty easy:

let jade = require('jade/lib/runtime');
app.engine('js', function (filePath, options, callback) { // define the template engine
  let data = require(filePath)(options, jade);
  callback(null, data);
});

Now we can change the view related settings in express:

app.set('view engine', 'js');
app.set('views', path.join(__dirname, 'views_js'));

Now responding to the first request on startup of the application takes 7ms for me. Just note that require throws error for files that do not exist so you can consider using try/catch or promises for handing the possible exceptions.

like image 107
undefined Avatar answered Sep 19 '22 14:09

undefined


You can use the jade-runtime module (unofficial). When you do require("jade-runtime") it seems to add a "jade" object to the GLOBAL object so it might just work like that.

What has worked for me is prepending the following code to a file that contains the (client-mode) compiled template function.

jade = require("jade-runtime").runtime
module.exports =

See https://github.com/meryn/make-document-html and its Makefile to see it work (the compiled, requirable template function ends up in lib/template.js). But it could all be done in Node.js itself as well.

edit

I need to add that, unless you want to prevent a Jade dependency (as I did), it's not necessary to work with precompiled template files to get the benefits of precompilation. You can just do templateFn = jade.compile(templateCode) somewhere and reuse that. In fact, I would be surprised if express does not cache the compiled templates itself when you do res.render(templateName, vars). It would be wasteful if it didn't. But I have no experience with express. Have you checked what Express render function does?

like image 22
Myrne Stol Avatar answered Sep 22 '22 14:09

Myrne Stol