—-> 原文地址

什么是模板引擎?

基于模板配合数据构造出字符串输出的一个组件(网页、Text、XML、Markdown等)

特点:

转义:

对特殊字符要转义,避免XSS攻击(防止变量中包含 js 脚本)

格式化:

对不同类型的变量要格式化,如货币需要变成12,345.00这样的格式,日期需要变成2016-01-01这样的格式

简单逻辑:

  1. {{ name }}同学,
  2. {% if score >= 90 %}
  3. 成绩优秀,应该奖励
  4. {% elif score >=60 %}
  5. 成绩良好,继续努力
  6. {% else %}
  7. 不及格,建议回家打屁股
  8. {% endif %}


简单使用:

  1. var res = nunjucks.render('foo.html');
  2. var res = nunjucks.render('foo.html', { username: 'James' });
  3. nunjucks.render('async.html', function(err, res) {
  4. });

深度定制版:

  1. const nunjucks = require('nunjucks');
  2. function createEnv(path, opts) {
  3. var
  4. autoescape = opts.autoescape === undefined ? true : opts.autoescape,
  5. noCache = opts.noCache || false,
  6. watch = opts.watch || false,
  7. throwOnUndefined = opts.throwOnUndefined || false,
  8. env = new nunjucks.Environment(
  9. new nunjucks.FileSystemLoader('views', {
  10. noCache: noCache, // 如果为 true,不使用缓存,模板每次都会重新编译
  11. watch: watch, // 如果为 true,当文件系统上的模板变化了,系统会自动更新他。使用前请确保已安装可选依赖 chokidar。
  12. }), {
  13. autoescape: autoescape,// (默认值: true) 控制输出是否被转义
  14. throwOnUndefined: throwOnUndefined, // (default: false) 当输出为 null 或 undefined 会抛出异常
  15. });
  16. if (opts.filters) {
  17. for (var f in opts.filters) {
  18. env.addFilter(f, opts.filters[f]); // 添加过滤器
  19. }
  20. }
  21. return env;
  22. }
  23. var env = createEnv('views', {
  24. watch: true,
  25. filters: {
  26. hex: function (n) {
  27. return '0x' + n.toString(16);
  28. }
  29. }
  30. });

注:

nunjucks 中的 filter (过滤器),一旦添加可以全局使用,结合 管道符 可以实现 vue 中的效果

views / index.html

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=<device-width>, initial-scale=1.0">
  6. <meta http-equiv="X-UA-Compatible" content="ie=edge">
  7. <title>Document</title>
  8. </head>
  9. <body>
  10. <h1>Hello {{ name }}</h1>
  11. {% for f in fruits %}
  12. <p>{{ f }}</p>
  13. {% endfor %}
  14. <p>{{ count|hex }}</p>
  15. </body>
  16. </html>
  1. var s = env.render('index.html', {
  2. name: '<Nunjucks>',
  3. fruits: ['Apple', 'Pear', 'Banana'],
  4. count: 1000
  5. });
  6. ...
  7. {{ 1000 | hex }} // 此处输出的不是 100,而是经过 过滤器处理后的 字符串 '0x3e8'
  8. ...

Nunjucks模板引擎最强大的功能在于模板的继承

网站的结构实际上是类似的,头部、尾部都是固定格式,只有中间页面部分内容不同。如果每个模板都重复头尾,一旦要修改头部或尾部,那就需要改动所有模板。


base.html

  1. <html><body>
  2. {% block header %} <h3>Unnamed</h3> {% endblock %}
  3. {% block body %} <div>No body</div> {% endblock %}
  4. {% block footer %} <div>copyright</div> {% endblock %}
  5. </body>

base.html定义了三个可编辑的块,分别命名为headerbodyfooter。子模板可以有选择地对块进行重新定义:

  1. {% extends 'base.html' %}
  2. {% block header %}<h1>{{ header }}</h1>{% endblock %}
  3. {% block body %}<p>{{ body }}</p>{% endblock %}

然后,我们对子模板进行渲染:

  1. console.log(env.render('extend.html', {
  2. header: 'Hello',
  3. body: 'bla bla bla...'
  4. }));

输出HTML如下:

  1. <html><body>
  2. <h1>Hello</h1>
  3. <p>bla bla bla...</p>
  4. <div>copyright</div> <-- footer没有重定义,所以仍使用父模板的内容
  5. </body>

性能

最后我们要考虑一下Nunjucks的性能。

  • 对于模板渲染本身来说,速度是非常非常快的,因为就是拼字符串嘛,纯CPU操作。
  • 性能问题主要出现在从文件读取模板内容这一步。这是一个IO操作,在Node.js环境中,我们知道,单线程的JavaScript最不能忍受的就是同步IO,但Nunjucks默认就使用同步IO读取模板文件。
  • 好消息是Nunjucks会缓存已读取的文件内容,也就是说,模板文件最多读取一次,就会放在内存中,后面的请求是不会再次读取文件的,只要我们指定了noCache: false这个参数。
  • 在开发环境下,可以关闭cache,这样每次重新加载模板,便于实时修改模板。在生产环境下,一定要打开cache,这样就不会有性能问题。
  • Nunjucks也提供了异步读取的方式,但是这样写起来很麻烦,有简单的写法我们就不会考虑复杂的写法。保持代码简单是可维护性的关键。