关于Handlebars.js模板引擎,你想知道的都在这里


Handlebars.js模板引擎是我们常用到的模板引擎,它是 JavaScript 一个语义模板库,通过对view和data的分离来快速构建web模板。它采用”Logic-less template”(无逻辑模版)的思路,在加载时被预编译,而不是到了客户端执行到代码时再去编译,这样可以保证模板加载和运行的速度。Handlebars兼容Mustache,你可以在Handlebars中导入Mustache模板。
本文对它的一些实用方法做了一些简单的总结,希望你在这里可以找到想用的方法。

使用与安装

Handlebars的安装非常简单,你只需要从Github下载最新版本,你也可访问下面网址获取最新信息:http://handlebarsjs.com。

目前handlebars.js已经被许多项目广泛使用了,handlebars是一个纯JS库,因此你可以像使用其他JS脚本一样用script标签来包含handlebars.js

1
<script type="text/javascript" src=".js/handlebars.js"></script>

基本语法

先来段实例看看

1
2
3
4
5
6
7
8
9
10
11
//html片段
<div id='demo'></div>
<script id="demo-template" type="text/x-handlebars-template">
<p>{{title}}</p>
</script>
//js片段 (这里以jquery的写法为例)
//预编译模板
var template = Handlebars.compile($("#demo-template").html());
//输入模板(data为需要渲染的数据,如:{"text":"Hello World!"})
$("#demo").html(template(data));

handlebars 表达式

Handlebars expressions是handlebars模板中最基本的单元,使用方法是加两个大括号{{ expression }}, handlebars模板会自动匹配相应的数值,对象甚至是函数。
上例中{{ title }}表示在上下文中找title属性,获取它的值。

点分割表达式

1
<h1>{{article.title}}</h1>

当前上下文找article属性,再找它的title属性

不转义表达式

1
{{{<p>Hello World!</P>}}}

当有些内容不想被转义时,需要使用3个大括号包{{{起来。

Handlebar的表达式

Block表达式

有时候当你需要对某条表达式进行更深入的操作时,Blocks就派上用场了,在Handlebars中,你可以在表达式后面跟随一个#号来表示Blocks,然后通过{{ 表达式 }}来结束Blocks。 如果当前的表达式是一个数组,则Handlebars会“自动展开数组”,并将Blocks的上下文设为数组中的元素。 例如:

1
2
3
4
5
<ul>
{{#programme}}
<li>{{language}}</li>
{{/programme}}
</ul>

有以下json数据

1
2
3
4
5
6
7
{
programme: [
{language: "JavaScript"},
{language: "HTML"},
{language: "CSS"}
]
}

编译模板代码会自动匹配programme数据并展开数据,渲染DOM后就是这样的

1
2
3
4
5
<ul>
<li>JavaScript</li>
<li>HTML</li>
<li>CSS</li>
</ul>

Handlebars的内置块表达式(Block helper)

1.each block helper

你可以使用内置的{{ each }} helper遍历列表块内容,用this来引用遍历的元素 例如:

1
2
3
4
5
<ul>
{{#each name}}
<li>{{this}}</li>
{{/each}}
</ul>

对应适用的json数据

1
2
3
{
name: ["html","css","javascript"]
}

这里的this指的是数组里的每一项元素,和上面的Block很像,但原理是不一样的这里的name是数组,而内置的each就是为了遍历数组用的,更复杂的数据也同样适用。

通过

1
{{@index}}

可以引用当前的循环索引

1
2
3
{{#each array}}
{{@index}}: {{this}}
{{/each}}


1
{{@key}}

可以引用当前的键名

1
2
3
{{#each object}}
{{@key}}: {{this}}
{{/each}}

数组迭代的第一步和最后一步用 @first@last 变量表示, 对象迭代时仅 @first 可用。

2.with block helper

{{ #with }}命令可以让当前的上下文进入到一个属性中,这个方法在操作复杂的template时候非常有用。

1
2
3
4
5
6
<div class="entry">
<h1>{{title}}</h1>
{{#with author}}
<h2>By {{firstName}} {{lastName}}</h2>
{{/with}}
</div>

对应适用json数据

1
2
3
4
5
6
7
{
title: "My first post!",
author: {
firstName: "Charles",
lastName: "Jolley"
}
}

渲染DOM后

1
2
3
4
<div class="entry">
<h1>My first post!</h1>
<h2>By Charles Jolley</h2>
</div>

with-进入到某个属性(进入到某个上下文环境):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
html片段
<table>
<thead>
<tr>
<th>姓名</th>
<th>性别</th>
<th>年龄</th>
<th>兴趣爱好</th>
</tr>
</thead>
<tbody id="tableList">
</tbody>
<script id="table-template" type="text/x-handlebars-template">
{{#each this}}
<tr>
<td>{{name}}</td>
<td>{{sex}}</td>
<td>{{age}}</td>
<td>
{{#with favorite}}
{{#each this}}
<p>{{name}}</p>
{{/each}}
{{/with}}
</td>
</tr>
{{/each}}
</script>
</table>

对应适用json数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
{
"name": "张三",
"sex": "0",
"age": 18,
"favorite":
[
{
"name":"唱歌"
},{
"name":"篮球"
}
]
},
{
"name": "李四",
"sex": "0",
"age": 22,
"favorite":
[
{
"name":"上网"
},{
"name":"足球"
}
]
}

with-终极this应用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
html片段
<table>
<thead>
<tr>
<th>姓名</th>
<th>性别</th>
<th>年龄</th>
<th>兴趣爱好</th>
</tr>
</thead>
<tbody id="tableList">
</tbody>
<script id="table-template" type="text/x-handlebars-template">
{{#each this}}
<tr>
<td>{{name}}</td>
<td>{{sex}}</td>
<td>{{age}}</td>
<td>
{{#with favorite}}
{{#each this}}
<p>{{this}}</p>
{{/each}}
{{/with}}
</td>
</tr>
{{/each}}
</script>
</table>

对应适用json数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{
"name": "张三",
"sex": "0",
"age": 18,
"favorite":
[
"唱歌",
"篮球"
]
},
{
"name": "李四",
"sex": "0",
"age": 22,
"favorite":
[
"上网",
"足球"
]
}

3.if else block helper

{{ #if }}就你使用JavaScript一样,你可以指定条件渲染DOM,如果它的参数返回false,undefined, null, “” 或者 [] (a “falsy” value), Handlebar将不会渲染DOM,如果存在{{ else }}则执行{{ else }}后面的渲染
例如:

1
2
3
4
5
6
7
8
9
{{#if list}}
<ul id="list">
{{#each list}}
<li>{{this}}</li>
{{/each}}
</ul>
{{else}}
<p>{{error}}</p>
{{/if}}

对应适用json数据

1
2
3
4
var data = {
info:['HTML5','CSS3',"WebGL"],
"error":"数据取出错误"
}

这里{{ #if }}判断是否存在list数组,如果存在则遍历list,如果不存在输出错误信息。

4.unless block helper

{{ unless }}这个语法是反向的if语法也就是当判断的值为false时他会渲染DOM 例如:

1
2
3
4
5
6
7
8
9
{{#unless data}}
<ul id="list">
{{#each list}}
<li>{{this}}</li>
{{/each}}
</ul>
{{else}}
<p>{{error}}</p>
{{/unless}}

Handlebars的访问(Path)

Handlebar支持路径和mustache,Handlebar还支持嵌套的路径,使得能够查找嵌套低于当前上下文的属性可以通过.来访问属性,也可以使用../,来访问父级属性。 例如:(使用.访问的例子)

1
<h1>{{author.id}}</h1>

对应json数据

1
2
3
4
5
6
7
8
{
title: "My First Blog Post!",
author: {
id: 47,
name: "Yehuda Katz"
},
body: "My first post. Wheeeee!"
}

例如:(使用../访问)

1
2
3
{{#with person}}
<h1>{{../company.name}}</h1>
{{/with}}

对应适用json数据

1
2
3
4
5
6
{
"person":
{ "name": "Alan" },
company:
{"name": "Rad, Inc." }
}

自定义helper

很多时候,我们需要更加复杂的if判断逻辑,显然默认的if不能满足我们的需求。这时你可以使用Handlebars.registerHelper()方法来注册一个helper
先看一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
html片段
<script id="table-template" type="text/x-handlebars-template">
{{#each student}}
{{#if name}}
{{#compare age 20}}
<tr>
<td>{{name}}</td>
<td>{{transformat sex}}</td>
<td>{{age}}</td>
</tr>
{{else}}
<tr>
<td>?</td>
<td>?</td>
<td>?</td>
</tr>
{{/compare}}
{{/if}}
{{/each}}
</script>
js片段:
//注册一个比较大小的Helper,判断v1是否大于v2
Handlebars.registerHelper("compare",function(v1,v2,options){
if(v1>v2){
//满足添加继续执行
return options.fn(this);
}else{
//不满足条件执行{{else}}部分
return options.inverse(this);
}
});
//注册一个翻译用的Helper,0翻译成男,1翻译成女
Handlebars.registerHelper("transformat",function(value){
if(value==0){
return "男";
}else if(value==1){
return "女";
}
});
json数据:
{
"student": [
{
"name": "张三",
"sex": "0",
"age": 23
},
{
"sex": "0",
"age": 22
},
{
"name": "妞妞",
"sex": "1",
"age": 18
}
]
}

本例中,利用Handlebars.js中Helper强大的扩展性,定义了一个compare,它用来比较两个数的大小,如果第一个数大于第二个数,满足条件继续执行,否则执行{{ else }}部分。

Handlebars.registerHelper用来定义Helper,它有两个参数,第一个参数是Helper名称,第二个参数是一个回调函数,用来执行核心业务逻辑。本例中的函数,有三个参数,其中前两个参数是需要比较的两个数,第三个参数是固定的,就叫options,如果加了该参数,就说明这个Helper是一个Block,块级别的Helper,有一定的语法结构,调用的时候加#号,就像if那样。

关于options的使用,我所了解的就是这种用法,return options.fn(this);表示满足条件继续执行,也就是执行{{# compare }}和{{ else }}之间的代码;return options.inverse(this);表示不满足条件,也就是执行{{ else }}和{{ /compare }}之间的代码。

由于这是一个块级别的Helper,所以调用要加#,例如:{{ #compare age 20 }},其中的age就是当前上下文中读取到的年龄,20是我随便写的值,意思是只要age比20大,就显示,否则全显示?。

可以看出,Helper中写的可以是任何js代码,现在想想,就知道Handlebars有多灵活了吧!!

上面注册的两个Helper,一个带options参数,另一个不带;带options参数的Helper是块级别的,而不带的,相当于行内级别的Helper。

从例子一开始,性别就是用0、1代码表示的,但实际情况下我们需要转换成汉字,transformat这个Helper需要一个参数,根据不同的代码,返回男女,这样调用{{ transformat sex }},其中sex是从当前上下文中读取的性别代码。

内建工具

转义字符串

1
Handlebars.Utils.escapeExpression(string)

判断空值

1
Handlebars.Utils.isEmpty(value)

扩展对象

1
Handlebars.Utils.extend(foo, {bar: true})

转字符串

1
Handlebars.Utils.toString(obj)

判断数组

1
Handlebars.Utils.isArray(obj)

判断函数

1
Handlebars.Utils.isFunction(obj)
文章目录
  1. 1. 使用与安装
  2. 2. 基本语法
    1. 2.1. handlebars 表达式
    2. 2.2. 点分割表达式
    3. 2.3. 不转义表达式
  3. 3. Handlebar的表达式
    1. 3.1. Block表达式
  4. 4. Handlebars的内置块表达式(Block helper)
    1. 4.1. 1.each block helper
    2. 4.2. 2.with block helper
      1. 4.2.1. with-进入到某个属性(进入到某个上下文环境):
      2. 4.2.2. with-终极this应用:
    3. 4.3. 3.if else block helper
    4. 4.4. 4.unless block helper
  5. 5. Handlebars的访问(Path)
  6. 6. 自定义helper
  7. 7. 内建工具
    1. 7.1. 转义字符串
    2. 7.2. 判断空值
    3. 7.3. 扩展对象
    4. 7.4. 转字符串
    5. 7.5. 判断数组
    6. 7.6. 判断函数
,