单页应用中,模板引擎是必备工具。这方面的选择比较丰富,旧轮子不断获得改进,新的轮子也不断被发明出来。我比较喜欢Handlebars,原因如下:
- 模板语言简单。继承自mustache,用
{{content}}
标记出要替换的内容即可。 - 包含逻辑判断和循环。在mustache基础上加强了这个部分,使得代码更易读。
- 不支持复杂的逻辑,尤其是嵌套JS。我认为这是模板引擎的关键,任何复杂的逻辑放在表现层都会使得系统不稳定。
- 支持预编译。可以加快实际运行的速度。
- 提供扩展功能,可以方便地增加自定义功能。这也是本文重点。
简单,是Handlebars的优势;但是原始版有点过分过头,它既缺少Angular模板中的过滤器,难以格式化输出数据;也没有足够的逻辑判断。使得我们必须在使用模板前进行大量预运算,逻辑据处理成模板能辨识的标记,把数据格式化成用户能识别的内容,这与代码复用的原则相违背。所幸Handlebars提供好用的扩展接口,给我们增添功能,适配业务逻辑的机会。
创建简单的格式化helper
Handlebars管实现功能的模块叫做Helper
,所以添加功能,就是创建新的helper
。于是我们需要用到registerHelper
方法。这个方法接受两个参数,第一个参数是helper
的名字,第二个参数则是负责完成功能的函数。函数中包含着每一个匹配出的值,最后以options
结尾。options
有一些系统API,比如index
、root
之类。
以下面这段代码为例,我创建了一个新的helper
,将表示分的数值格式化成常用的以元为单位的字符串:
Handlebars.registerHelper('d100', function (value) {
return (value / 100).toFixed(2);
});
// 使用时非常简单
var str = '消费{{d100 rmb}}元。'
, template = Handlebars.compile(str)
, html = template({rmb: 1000});
document.body.append(html); // 消费10.00元
这样,便创建了最简单的helper
,我称其为“格式化helper
”,主要用来格式化数据输出,比如截取字符串,提供可读数字等等。
创建逻辑helper
除了将存储信息格式化成可读信息,还要强化逻辑功能,才能满足日常所需。Handlebars默认只提供if...else...
逻辑,然而实际业务中,常常需要根据对象的不同状态给出不同的操作选项,或者呈现不同的样式。这个时候,增加一个in
逻辑是当务之急。
回到Handlebars的文档,嗯,它给我们提供了扩展的接口。
var pop = Array.prototype.pop
, slice = Array.prototype.slice;
Handlebars.registerHelper('in', function (value, array, options) {
// 先判断array是否作为判断依据
if (!Array.isArray(array)) {
options = pop.call(arguments);
array = slice.call(arguments, 1);
}
// 然后就可以用indexOf判断了,不过需要注意,它是全等的
if (array.indexOf(value) !== -1) {
return options.fn(this);
}
return options.inverse(this);
});
使用静态常量
Handlebars除了可以从数据对象里取值,还可以手工写入一些值作为参数,比如我们要做“等于”判断时,就可以这样:
Handlebars.registerHelper('equal', function (value, target, options) {
if (value === target) {
return options.fn(this);
} else {
return option
}.
});
使用的时候,需要使用引号以便于引用值区分开;如果是纯数字的话就无所谓了。
{{#equal a 1}}等于1{{/equal}}
{{#eqaul b 'b'}}等于b{{/equal}}
使用Hash参数
出于某种我猜不到的原因,Handlebars还支持在模板中像HTML节点属性那样写Hash参数——我觉得无论从使用效率上还是开发解析方法,这种类似属性写法都不如前面说的静态常量方便。
它写起来是这样的:
{{img src class="img"}}
就是像一般的HTML标签那样。然后注册helper
的时候就可以这样用:
Handlebars.registerHelper('ext', function (value, options) {
return '<img src="' + value + '" class="' + options.hash.class + '" >';
}
好吧,至少暂时我还没发现这个功能有啥特别亮眼的用法。
总结
加强之后的Handlerbars用起来舒服多了。附上我目前用到的自定义函数:Github链接。
本文主要参考自Handlebars的官方文档。
欢迎吐槽,共同进步