什么是模板引擎?
基于模板配合数据构造出字符串输出的一个组件(网页、Text、XML、Markdown等)
特点:
转义:
对特殊字符要转义,避免XSS攻击(防止变量中包含 js 脚本)
格式化:
对不同类型的变量要格式化,如货币需要变成
12,345.00这样的格式,日期需要变成2016-01-01这样的格式
简单逻辑:
{{ name }}同学,{% if score >= 90 %}成绩优秀,应该奖励{% elif score >=60 %}成绩良好,继续努力{% else %}不及格,建议回家打屁股{% endif %}
简单使用:
var res = nunjucks.render('foo.html');var res = nunjucks.render('foo.html', { username: 'James' });nunjucks.render('async.html', function(err, res) {});
深度定制版:
const nunjucks = require('nunjucks');function createEnv(path, opts) {varautoescape = opts.autoescape === undefined ? true : opts.autoescape,noCache = opts.noCache || false,watch = opts.watch || false,throwOnUndefined = opts.throwOnUndefined || false,env = new nunjucks.Environment(new nunjucks.FileSystemLoader('views', {noCache: noCache, // 如果为 true,不使用缓存,模板每次都会重新编译watch: watch, // 如果为 true,当文件系统上的模板变化了,系统会自动更新他。使用前请确保已安装可选依赖 chokidar。}), {autoescape: autoescape,// (默认值: true) 控制输出是否被转义throwOnUndefined: throwOnUndefined, // (default: false) 当输出为 null 或 undefined 会抛出异常});if (opts.filters) {for (var f in opts.filters) {env.addFilter(f, opts.filters[f]); // 添加过滤器}}return env;}var env = createEnv('views', {watch: true,filters: {hex: function (n) {return '0x' + n.toString(16);}}});
注:
nunjucks 中的 filter (过滤器),一旦添加可以全局使用,结合 管道符 可以实现 vue 中的效果
views / index.html
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=<device-width>, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title></head><body><h1>Hello {{ name }}</h1>{% for f in fruits %}<p>{{ f }}</p>{% endfor %}<p>{{ count|hex }}</p></body></html>
var s = env.render('index.html', {name: '<Nunjucks>',fruits: ['Apple', 'Pear', 'Banana'],count: 1000});...{{ 1000 | hex }} // 此处输出的不是 100,而是经过 过滤器处理后的 字符串 '0x3e8'...
Nunjucks模板引擎最强大的功能在于模板的继承
网站的结构实际上是类似的,头部、尾部都是固定格式,只有中间页面部分内容不同。如果每个模板都重复头尾,一旦要修改头部或尾部,那就需要改动所有模板。
base.html
<html><body>{% block header %} <h3>Unnamed</h3> {% endblock %}{% block body %} <div>No body</div> {% endblock %}{% block footer %} <div>copyright</div> {% endblock %}</body>
base.html定义了三个可编辑的块,分别命名为header、body和footer。子模板可以有选择地对块进行重新定义:
{% extends 'base.html' %}{% block header %}<h1>{{ header }}</h1>{% endblock %}{% block body %}<p>{{ body }}</p>{% endblock %}
然后,我们对子模板进行渲染:
console.log(env.render('extend.html', {header: 'Hello',body: 'bla bla bla...'}));
输出HTML如下:
<html><body><h1>Hello</h1><p>bla bla bla...</p><div>copyright</div> <-- footer没有重定义,所以仍使用父模板的内容</body>
性能
最后我们要考虑一下Nunjucks的性能。
- 对于模板渲染本身来说,速度是非常非常快的,因为就是拼字符串嘛,纯CPU操作。
- 性能问题主要出现在从文件读取模板内容这一步。这是一个IO操作,在Node.js环境中,我们知道,单线程的JavaScript最不能忍受的就是同步IO,但Nunjucks默认就使用同步IO读取模板文件。
- 好消息是Nunjucks会缓存已读取的文件内容,也就是说,模板文件最多读取一次,就会放在内存中,后面的请求是不会再次读取文件的,只要我们指定了
noCache: false这个参数。 - 在开发环境下,可以关闭cache,这样每次重新加载模板,便于实时修改模板。在生产环境下,一定要打开cache,这样就不会有性能问题。
- Nunjucks也提供了异步读取的方式,但是这样写起来很麻烦,有简单的写法我们就不会考虑复杂的写法。保持代码简单是可维护性的关键。
