Handlebars.js入门教程

概述

刚接触前端的时候,师傅就给我推荐了Handlebars,自己也蛮喜欢它的语法。到现在,Handlebars都已经更新到3.0.3了,是时候重新过一遍文档了。

引入

要使用Handlebars,首先你得download,然后再页面引入,就像这样

<script src="script/lib/jquery.js">script>
<script src="script/lib/handlebars.js">script>

如果你使用了模块化的管理工具,如requirejs、webpack、seajs,不用担心。Handlebars是支持amd、cmd规范的,使用就像这样

var Handlebars=require('Handlebars');

基本

来一个简单的例子,向服务器发起了一个ajax请求获取了一个对象数组,要渲染到页面上。数据格式是这样的

var data = [
    {
        name: 'xxx',
        age: 10
    },
    {
        name: 'zzz',
        age: 12
    },
    {
        name: 'yyy',
        age: 9
    }
];

首先我们要建立一个模板结构,也就是我们的Html,为了展示和逻辑分离,我们不应该将模版内容放到js当中,先看下不好的做法:

var $container = $('#container');//容器
var content = '';

data.forEach(function (item) {
    content += '

' + item.name + ':' + item.age + '

'
; }); $container.html(content);

当Html内容一多,各种单引号,双引号,可读性、结构性太差。维护起来将是我们开发的噩梦。
使用Handlebars,首先我们将Html抽出来,就像用script标签包裹起来,放入我们当前的页面中,就像这样

<body>
    <div id="container">div>
    <script type="text/x-handlebars-template" id="template-user">{{#each this}}
            <p>{{name}}:{{age}}p>
        {{/each}}script>
    <script src="script/lib/jquery.js">script>
    <script src="script/lib/handlebars.js">script>
    <script src="script/my/basicuse.js">script>
body>

记得改变type类型,这样浏览器就不会把标签里的内容当作js执行。然后编写我们的代码

var $container = $('#container');//容器
var source = $('#template-user').html();//获取到html结构
var template = Handlebars.compile(source);//编译成模板
var html = template(data);//生成完成的html结构
$container.html(html);//插入dom

Handlebars的基本使用就如上了,用{{ }}输出内容。记住了

模板最外层的this就是你调用template方法时传入的对象

Block

我们使用模板一般都是为了遍历对象结构,然后渲染到页面上。有人说了如果我就传递个字符串进去呢?直接

$container.html(str);

用JBM模板!使用模板最常用的就是if判断和each遍历了,下面来详细讲解。

Handlebars的block都是这种{{#each}}{{/each}}的闭合结构

if/unless

if

Handlebars的if判断只能判断true和false,没办法进行这种a===3的逻辑判断。它的设定就是如此,它认为逻辑判断的内容不应该出现在模板中。看个例子

#template   
{{#if isEdit}}
    <p>isEditp>
{{/if}}{{#if email}}
    <p>{{email}}p>
{{/if}}{{#if num}}
    <p>{{num}}p>
{{/if}}   

#数据
var data = {
    isEdit: true,
    email: '',
    num: '0'
};

#页面效果
isEdit
0

Handlebars if在判断前会做类型转换,如''、undefined、null、0、[]等都会被识别为false。而实际情况下我们都用数字来标识不同的状态,碰到这种数据我们需要预处理下,才能渲染哦。

if else

#template    
{{#if isEdit}}
    <p>isEditp>
{{else}}
    <p>isNotEditp>
{{/if}}   

#数据
var data = {
    isEdit: false
};

#页面效果
isNotEdit

看看多分支是咋子写的

#template   
{{#if isEdit}}
    <p>isEditp>
{{else if isRead}}
    <p>isNotEdit isReadp>
{{else}}
    <p>isNotReadp>
{{/if}}   

#数据
var data = {
    isEdit: false,
    isRead: false
};

#页面效果
isNotRead

unless

作用刚好跟if相反,if是true的时候返回,unless是false的时候返回,看例子

#template    
{{#unless isEdit}}
    <p>isNotEditp>
{{else unless isRead}}
    <p>isReadp>
{{else}}
    <p>isNotReadp>
{{/unless}}   

#数据
var data = {
    isEdit: true,
    isRead:true
};

#页面效果
isNotRead

each

遍历数组

#template
{{#each this}}
    <p>{{this.name}}:{{this.age}}p>
{{else}}
    <p>no datap>    
{{/each}}

#数据
var data = [
    {
        name: 'yyy',
        age: 23
    },
    {
        name: 'zzz',
        age: 55
    }
];

#页面效果
yyy:23
zzz:55

each也支持else的判断。each里面的this是指向单个对象的,这个时候this可以省略不写,效果是一样的

{{#each this}}
    <p>{{name}}:{{age}}p>
{{/each}}

遍历数组的时候一般都会输出序号,怎么破?

#template
{{#each this}}
    <p>{{this.name}}:{{this.age}}p>
{{/each}}   

#页面效果
0 yyy:23
1 zzz:55

通过@index或者@key都可以获得序号,但是序号都是从0开始的,这个比较坑!如果要从1开始,得自己写个helper处理,真实日了狗了!

遍历数组的需要判别是第一个还是最后一个怎么破?

#template
{{#each this}}
    <p>{{name}}:{{age}}{{#if @first}}first{{/if}}{{#if @last}}last{{/if}}p>
{{/each}}

#页面效果
yyy:23 first
zzz:55 last

通过@first和@last可以判断是否是数组的第一个或者最后一个。

如果在加上@odd、@even就完美了!

遍历对象

真实的应用场景下,服务器很可能会返回一个map,就是js当中的对象,这个时候我们是不知道有哪些key的,如何遍历这个map呢?

#template
{{#each this}}
    <p>{{@key}}:{{this}}p>
{{/each}}

#数据
var data = {
    name: 'yyy',
    age: 23
};

#页面效果
name:yyy
age:23

通过@key可以获取到对象的key名称。

Html转义

假想这样一个场景,通过ajax获取到了一段富文本内容,然后展示在页面中

#template
<div>{{richText}}div>

#数据
var data = {
    richText: '<div>this is rich textdiv>'
};

#页面效果
<div>this is rich textdiv>

这个时候你肯定会想了,真实日了狗了,怎么原样输出了,没有解析成html啊。因为{{richText}}的输出默认转义Html,几乎所有的模板引擎输出默认都是转义Html的,避免xss攻击。如果你想避免转义,请这样用

{{{richText}}}

Helpers

列表输出的时候,如果有时间字段,一般都需要格式化时间,拿到数据后我们还得处理

#template
{{#each this}}
    

{{name}}:{{addTime}}

{{/each}} #js var data = [ { name: 'xxx', addTime: new Date() }, { name: 'zzz', addTime: new Date() } ]; data.forEach(function(item){ item.addTime=moment(item.addTime).format('YYYY-MM-DD'); }); #页面效果 xxx:2015-05-26 zzz:2015-05-26

换个页面碰到类似的情景,相同的代码又得写一面,冗余的代码太多了,不利于后期维护。怎么破?

#template
{{#each this}}
    

{{name}}:{{moment addTime}}

{{/each}} #js Handlebars.registerHelper('moment', function (date, options) { var formatStr = options.hash.format || 'YYYY-MM-DD'; return new Handlebars.SafeString(moment(date).format(formatStr)); }); var data = [ { name: 'xxx', addTime: new Date() }, { name: 'zzz', addTime: new Date() } ];

注册一个全局的moment,这样所有的时间格式化,都可以通过{{moment time}}调用,维护的成本大大降低。
需要注意的是helper如{{moment arg1 arg2}}的形式最多添加两个参数可以被注册函数获取到,如果要添加多个参数,请使用hash的形式

#template

{{query name 'arg2' hash1='hash1' hash2='hash2'}}

#数据 Handlebars.registerHelper('query', function (arg1, arg2, options) { console.log('arg1:' + arg1); console.log('arg2:' + arg2); console.log(options.hash); }); var data = { name: 'jacky' }; #控制台 $ arg1:jacky $ arg2:arg2 $ Object {hash2: "hash2", hash1: "hash1"}

Handlebars.SafeString就是不转义Html,如果想转义Html直接return内容即可。

#template

{{safe}}

#js Handlebars.registerHelper('safe', function () { return new Handlebars.SafeString('
safe string
'
) }); #页面效果 safe string

Partials

共享同一个模板内容,后端渲染使用的比较多

#template

{{> footer}}

#js Handlebars.registerPartial('footer', function () { return new Handlebars.SafeString('
This is footer
'
) }); var data = { name: 'jacky' }; #页面效果 This is footer

../

这样一个数据结构渲染到页面上

#template
{{#each company.prodList}}
    <p>{{prodName}}{{company.comName}}p>
{{/each}}

#js
var data = {
    company: {
        comName: '技术有限公司',
        prodList: [
            {
                prodName: '产品1'
            },
            {
                prodName: '产品2'
            }
        ]
    }
};

#页面效果
产品1
产品2

等等好像有点不对劲啊,为啥没有公司名称呢?前面说到each里面的this都是指向单个对象的,{{prodName}} {{company.comName}}这种写法省略了this,还原下

{{#each company.prodList}}
    <p>{{this.prodName}}{{this.company.comName}}p>
{{/each}}

知道问题在哪里了吧。怎么破?

{{#each company.prodList}}
    <p>{{prodName}}{{../company.comName}}p>
{{/each}}

通过../回到each之外。下面来填另一个经典的坑

#template
<ul>
    {{#each this}}
        <li>
            <ul>
                {{#each this}}
                    <li>{{@../index}}-{{@index}}{{this}}li>
                {{/each}}
            ul>
        li>
    {{/each}}
ul>

#js
var data = [
    ['aaa', 'bbb', 'ccc'],
    ['ddd', 'eee', 'fff']
];

#页面效果
0-0 aaa
0-1 bbb
0-2 ccc
1-0 ddd
1-1 eee
1-2 fff

代码链接

Github

参考

Handlebars

概述

刚接触前端的时候,师傅就给我推荐了Handlebars,自己也蛮喜欢它的语法。到现在,Handlebars都已经更新到3.0.3了,是时候重新过一遍文档了。

引入

要使用Handlebars,首先你得download,然后再页面引入,就像这样

  1.  
    <script src="script/lib/jquery.js"> script>
  2.  
    <script src="script/lib/handlebars.js"> script>

如果你使用了模块化的管理工具,如requirejs、webpack、seajs,不用担心。Handlebars是支持amd、cmd规范的,使用就像这样

var Handlebars=require('Handlebars');

基本

来一个简单的例子,向服务器发起了一个ajax请求获取了一个对象数组,要渲染到页面上。数据格式是这样的

  1.  
    var data = [
  2.  
    {
  3.  
    name: 'xxx',
  4.  
    age: 10
  5.  
    },
  6.  
    {
  7.  
    name: 'zzz',
  8.  
    age: 12
  9.  
    },
  10.  
    {
  11.  
    name: 'yyy',
  12.  
    age: 9
  13.  
    }
  14.  
    ];

首先我们要建立一个模板结构,也就是我们的Html,为了展示和逻辑分离,我们不应该将模版内容放到js当中,先看下不好的做法:

  1.  
    var $container = $( '#container'); //容器
  2.  
    var content = '';
  3.  
     
  4.  
    data. forEach( function (item) {
  5.  
    content += '

    ' + item.name + ':' + item.age + '

    '
    ;
  6.  
    });
  7.  
     
  8.  
    $container.html(content);

当Html内容一多,各种单引号,双引号,可读性、结构性太差。维护起来将是我们开发的噩梦。
使用Handlebars,首先我们将Html抽出来,就像用script标签包裹起来,放入我们当前的页面中,就像这样

  1.  
    <body>
  2.  
    <div id="container"> div>
  3.  
    <script type="text/x-handlebars-template" id="template-user">
  4.  
    {{#each this}}
  5.  
    <p>{{name}}:{{age}} p>
  6.  
    {{/each}}
  7.  
    script>
  8.  
    <script src="script/lib/jquery.js"> script>
  9.  
    <script src="script/lib/handlebars.js"> script>
  10.  
    <script src="script/my/basicuse.js"> script>
  11.  
    body>

记得改变type类型,这样浏览器就不会把标签里的内容当作js执行。然后编写我们的代码

  1.  
    var $container = $( '#container'); //容器
  2.  
    var source = $( '#template-user').html(); //获取到html结构
  3.  
    var template = Handlebars.compile(source); //编译成模板
  4.  
    var html = template(data); //生成完成的html结构
  5.  
    $container.html(html); //插入dom

Handlebars的基本使用就如上了,用{{ }}输出内容。记住了

模板最外层的this就是你调用template方法时传入的对象

Block

我们使用模板一般都是为了遍历对象结构,然后渲染到页面上。有人说了如果我就传递个字符串进去呢?直接

$container.html(str);

用JBM模板!使用模板最常用的就是if判断和each遍历了,下面来详细讲解。

Handlebars的block都是这种{{#each}}{{/each}}的闭合结构

if/unless

if

Handlebars的if判断只能判断true和false,没办法进行这种a===3的逻辑判断。它的设定就是如此,它认为逻辑判断的内容不应该出现在模板中。看个例子

  1.  
    #template
  2.  
    {{ #if isEdit}}
  3.  

    isEdit

  4.  
    {{/ if}}
  5.  
    {{ #if email}}
  6.  

    {{email}}

  7.  
    {{/ if}}
  8.  
    {{ #if num}}
  9.  

    {{num}}

  10.  
    {{/ if}}
  11.  
     
  12.  
    #数据
  13.  
    var data = {
  14.  
    isEdit: true,
  15.  
    email: '',
  16.  
    num: '0'
  17.  
    };
  18.  
     
  19.  
    #页面效果
  20.  
    isEdit
  21.  
    0

Handlebars if在判断前会做类型转换,如''、undefined、null、0、[]等都会被识别为false。而实际情况下我们都用数字来标识不同的状态,碰到这种数据我们需要预处理下,才能渲染哦。

if else

  1.  
    #template
  2.  
    {{ #if isEdit}}
  3.  

    isEdit

  4.  
    {{ else}}
  5.  

    isNotEdit

  6.  
    {{/ if}}
  7.  
     
  8.  
    #数据
  9.  
    var data = {
  10.  
    isEdit: false
  11.  
    };
  12.  
     
  13.  
    #页面效果
  14.  
    isNotEdit

看看多分支是咋子写的

  1.  
    #template
  2.  
    {{ #if isEdit}}
  3.  

    isEdit

  4.  
    {{ else if isRead}}
  5.  

    isNotEdit isRead

  6.  
    {{ else}}
  7.  

    isNotRead

  8.  
    {{/ if}}
  9.  
     
  10.  
    #数据
  11.  
    var data = {
  12.  
    isEdit: false,
  13.  
    isRead: false
  14.  
    };
  15.  
     
  16.  
    #页面效果
  17.  
    isNotRead

unless

作用刚好跟if相反,if是true的时候返回,unless是false的时候返回,看例子

  1.  
    #template
  2.  
    {{ #unless isEdit}}
  3.  

    isNotEdit

  4.  
    {{ else unless isRead}}
  5.  

    isRead

  6.  
    {{ else}}
  7.  

    isNotRead

  8.  
    {{/unless}}
  9.  
     
  10.  
    #数据
  11.  
    var data = {
  12.  
    isEdit: true,
  13.  
    isRead: true
  14.  
    };
  15.  
     
  16.  
    #页面效果
  17.  
    isNotRead

each

遍历数组

  1.  
    #template
  2.  
    {{#each this}}
  3.  

    {{ this.name}}:{{ this.age}}

  4.  
    {{ else}}
  5.  

    no data

  6.  
    {{/each}}
  7.  
     
  8.  
    #数据
  9.  
    var data = [
  10.  
    {
  11.  
    name: 'yyy',
  12.  
    age: 23
  13.  
    },
  14.  
    {
  15.  
    name: 'zzz',
  16.  
    age: 55
  17.  
    }
  18.  
    ];
  19.  
     
  20.  
    #页面效果
  21.  
    yyy: 23
  22.  
    zzz: 55

each也支持else的判断。each里面的this是指向单个对象的,这个时候this可以省略不写,效果是一样的

  1.  
    {{# each this}}
  2.  

    {{name}}:{{age}}

  3.  
    {{/ each}}

遍历数组的时候一般都会输出序号,怎么破?

  1.  
    #template
  2.  
    {{#each this}}
  3.  

    {{ this.name}}:{{ this.age}}

  4.  
    {{/each}}
  5.  
     
  6.  
    #页面效果
  7.  
    0 yyy: 23
  8.  
    1 zzz: 55

通过@index或者@key都可以获得序号,但是序号都是从0开始的,这个比较坑!如果要从1开始,得自己写个helper处理,真实日了狗了!

遍历数组的需要判别是第一个还是最后一个怎么破?

  1.  
    #template
  2.  
    {{ #each this}}
  3.  

    {{name}}:{{age}} {{ #if @first}}first{{/if}} {{#if @last}}last{{/if}}

  4.  
    {{/each}}
  5.  
     
  6.  
    #页面效果
  7.  
    yyy: 23 first
  8.  
    zzz: 55 last

通过@first和@last可以判断是否是数组的第一个或者最后一个。

如果在加上@odd、@even就完美了!

遍历对象

真实的应用场景下,服务器很可能会返回一个map,就是js当中的对象,这个时候我们是不知道有哪些key的,如何遍历这个map呢?

  1.  
    #template
  2.  
    {{#each this}}
  3.  

    {{ @key}}:{{ this}}

  4.  
    {{/each}}
  5.  
     
  6.  
    #数据
  7.  
    var data = {
  8.  
    name: 'yyy',
  9.  
    age: 23
  10.  
    };
  11.  
     
  12.  
    #页面效果
  13.  
    name:yyy
  14.  
    age: 23

通过@key可以获取到对象的key名称。

Html转义

假想这样一个场景,通过ajax获取到了一段富文本内容,然后展示在页面中

  1.  
    #template
  2.  
    {{richText}}
  3.  
     
  4.  
    #数据
  5.  
    var data = {
  6.  
    richText: '
    this is rich text
    '
  7.  
    };
  8.  
     
  9.  
    #页面效果
  10.  
    this is rich text

这个时候你肯定会想了,真实日了狗了,怎么原样输出了,没有解析成html啊。因为{{richText}}的输出默认转义Html,几乎所有的模板引擎输出默认都是转义Html的,避免xss攻击。如果你想避免转义,请这样用

{{{richText}}}

Helpers

列表输出的时候,如果有时间字段,一般都需要格式化时间,拿到数据后我们还得处理

  1.  
    #template
  2.  
    {{ #each this}}
  3.  

    {{name}}:{{addTime}}

  4.  
    {{/each}}
  5.  
     
  6.  
    #js
  7.  
    var data = [
  8.  
    {
  9.  
    name: 'xxx',
  10.  
    addTime: new Date()
  11.  
    },
  12.  
    {
  13.  
    name: 'zzz',
  14.  
    addTime: new Date()
  15.  
    }
  16.  
    ];
  17.  
     
  18.  
    data. forEach( function(item){
  19.  
    item.addTime=moment(item.addTime).format( 'YYYY-MM-DD');
  20.  
    });
  21.  
     
  22.  
    #页面效果
  23.  
    xxx: 2015 -05 -26
  24.  
    zzz: 2015 -05 -26

换个页面碰到类似的情景,相同的代码又得写一面,冗余的代码太多了,不利于后期维护。怎么破?

  1.  
    #template
  2.  
    {{ #each this}}
  3.  

    {{name}}:{{moment addTime}}

  4.  
    {{/each}}
  5.  
     
  6.  
    #js
  7.  
    Handlebars.registerHelper( 'moment', function (date, options) {
  8.  
    var formatStr = options.hash.format || 'YYYY-MM-DD';
  9.  
    return new Handlebars.SafeString(moment(date).format(formatStr));
  10.  
    });
  11.  
     
  12.  
    var data = [
  13.  
    {
  14.  
    name: 'xxx',
  15.  
    addTime: new Date()
  16.  
    },
  17.  
    {
  18.  
    name: 'zzz',
  19.  
    addTime: new Date()
  20.  
    }
  21.  
    ];

注册一个全局的moment,这样所有的时间格式化,都可以通过{{moment time}}调用,维护的成本大大降低。
需要注意的是helper如{{moment arg1 arg2}}的形式最多添加两个参数可以被注册函数获取到,如果要添加多个参数,请使用hash的形式

  1.  
    #template
  2.  

    {{query name 'arg2' hash1= 'hash1' hash2= 'hash2'}}

  3.  
     
  4.  
    #数据
  5.  
    Handlebars.registerHelper( 'query', function (arg1, arg2, options) {
  6.  
    console.log( 'arg1:' + arg1);
  7.  
    console.log( 'arg2:' + arg2);
  8.  
    console.log(options.hash);
  9.  
    });
  10.  
     
  11.  
    var data = {
  12.  
    name: 'jacky'
  13.  
    };
  14.  
     
  15.  
    #控制台
  16.  
    $ arg1:jacky
  17.  
    $ arg2:arg2
  18.  
    $ Object {hash2: "hash2", hash1: "hash1"}

Handlebars.SafeString就是不转义Html,如果想转义Html直接return内容即可。

  1.  
    #template
  2.  

    {{safe}}

  3.  
     
  4.  
    #js
  5.  
    Handlebars.registerHelper( 'safe', function () {
  6.  
    return new Handlebars.SafeString( '
    safe string
    '
    )
  7.  
    });
  8.  
     
  9.  
    #页面效果
  10.  
    safe string

Partials

共享同一个模板内容,后端渲染使用的比较多

  1.  
    #template
  2.  

    {{> footer}}

  3.  
     
  4.  
    #js
  5.  
    Handlebars.registerPartial( 'footer', function () {
  6.  
    return new Handlebars.SafeString( '
    This is footer
    '
    )
  7.  
    });
  8.  
     
  9.  
    var data = {
  10.  
    name: 'jacky'
  11.  
    };
  12.  
     
  13.  
    #页面效果
  14.  
    This is footer

../

这样一个数据结构渲染到页面上

  1.  
    #template
  2.  
    {{ #each company.prodList}}
  3.  

    {{prodName}} {{company.comName}}

  4.  
    {{/each}}
  5.  
     
  6.  
    #js
  7.  
    var data = {
  8.  
    company: {
  9.  
    comName: '技术有限公司',
  10.  
    prodList: [
  11.  
    {
  12.  
    prodName: '产品1'
  13.  
    },
  14.  
    {
  15.  
    prodName: '产品2'
  16.  
    }
  17.  
    ]
  18.  
    }
  19.  
    };
  20.  
     
  21.  
    #页面效果
  22.  
    产品 1
  23.  
    产品 2

等等好像有点不对劲啊,为啥没有公司名称呢?前面说到each里面的this都是指向单个对象的,{{prodName}} {{company.comName}}这种写法省略了this,还原下

  1.  
    {{#each company.prodList}}
  2.  

    {{ this.prodName}} {{ this.company.comName}}

  3.  
    {{/each}}

知道问题在哪里了吧。怎么破?

  1.  
    {{# each company.prodList}}
  2.  

    {{prodName}} {{../company.comName}}

  3.  
    {{/ each}}

通过../回到each之外。下面来填另一个经典的坑

  1.  
    #template
  2.  
  3.  
    {{#each this}}
  4.  
  •  
  •  
    {{#each this}}
  •  
  • {{@../index}}-{{ @index}} {{ this}}
  •  
    {{/each}}
  •  
  •  
  •  
    {{/each}}
  •  
  •  
     
  •  
    #js
  •  
    var data = [
  •  
    [ 'aaa', 'bbb', 'ccc'],
  •  
    [ 'ddd', 'eee', 'fff']
  •  
    ];
  •  
     
  •  
    #页面效果
  •  
    0 -0 aaa
  •  
    0 -1 bbb
  •  
    0 -2 ccc
  •  
    1 -0 ddd
  •  
    1 -1 eee
  •  
    1 -2 fff
  • 代码链接

    Github

    参考

    Handlebars

    你可能感兴趣的:(Handlebars.js入门教程)