Handlebars模板库浅析

Handlebars模板库简单介绍

Handlebars是JavaScript一个语义模板库,通过对view(模板)和data(ajax请求的数据,一般是json)的分离来快速构建Web模板。它采用"Logic-less template"(无逻辑模版)的思路,在加载时被预编译(先对view进行编译生成模板,之后只要把json数据套进去就行了),而不是到了客户端执行到代码时再去编译,这样可以保证模板加载和运行的速度。Handlebars兼容Mustache,你可以在Handlebars中导入Mustache模板。

Handlebars expressions是handlebars模板中最基本的单元,使用方法是加两个花括号{{value}}, handlebars模板会自动匹配相应的数值,对象甚至是函数。

当你想要复用模板的一部分,或者将长模板分割成为多个模板方便维护时,partials就派上用场了。

通过{{}}取出来的内容(把json的数据取到后,显示在模板中时),都会经过编码,也就是说,如果取出的内容中包含html标签,会被转码成纯文本,不会被当成html解析,实际上就是做了类似这样的操作:把<用&lt;替代。这样做是很好的,既可以显示html代码(转码后的html),又可以避免xss注入(避免显示的内容是script,或者href,img等有跨站脚本攻击的标签)。这个功能在做代码展示的时候是非常有用的。但是有时候我们可能需要解析html,不要转码,很简单,把{{}}换成{{{}}}就可以啦。

Handlebars模板库简单使用

<!DOCTYPE html>
 <html>
   <head>
       <title>Handlebars Expressions Example</title>
   </head>
   <body>
       <h1>Handlebars Expressions Example!</h1>
       <div id="list">
       </div>

  <script type="text/javascript" src="script/jquery.js"></script>     //引入jquery插件
       <script type="text/javascript" src="script/handlebars-1.0.0.beta.6.js"></script>    //引入handlebars模板库

  <script id="people-template" type="text/x-handlebars-template">      //显示在页面上的标准模板
         {{#each people}}
      <div class="person">
                 <h2>{{first_name}} {{last_name}}</h2>
                 <div class="phone">{{phone}}</div>
                 <div class="email"><a href="mailto:{{email}}">{{email}}</a></div>
                 <div class="since">User since {{member_since}}</div>
           </div>
         {{/each}}
       </script>

  <script type="text/javascript">
           $(document).ready(function() {    //页面加载完成后,执行
    
               var template = Handlebars.compile($("#people-template").html());

         //先取到标准模板库的元素,然后调用html方法,得到它的内容。由于它的内容是一个handlebars模板,所以可以用Handlebars.compile对这个模板进行预编译。这里讲下jQuery对象html方法:$().html(),取第一个匹配元素的内容。$().html("chaojidan"),设置所有匹配元素的内容。$().html(function(index,content){  return newContent; }) ,index是匹配元素的位置,content是匹配元素的内容,newContent是替换匹配元素内容的新内容。

      var data = {   //后台通过ajax请求到的数据
                 people: [
                     { first_name: "Alan", last_name: "Johnson", phone: "1234567890", email: "alan@test.com", member_since: "Mar 25, 2011" },
                     { first_name: "Allison", last_name: "House", phone: "0987654321", email: "allison@test.com", member_since: "Jan 13, 2011" },
              { first_name: "Nick", last_name: "Pettit", phone: "9836592272", email: "nick@test.com", member_since: "Apr 9, 2009" },
                     { first_name: "Jim", last_name: "Hoskins", phone: "7284927150", email: "jim@test.com", member_since: "May 21, 2010" },
                     { first_name: "Ryan", last_name: "Carson", phone: "8263729224", email: "ryan@test.com", member_since: "Nov 1, 2008" }
                 ]
               };

      $('#list').html(template(data));   //把data对象传进去,模板会自动去匹配数据。模板中取得是data.people属性的值。并且对people循环处理,然后把people数组中的每一项进行输出。最后显示在页面中。
           });
       </script>
   </body>
 </html>

web 开发中,js 解析JSON 是经常的事情。非常繁琐。handlebars 使用了模版,只要你定义一个模版,提供一个json对象,handlebars 就能把json对象放到你定的模版中,非常方便好用!

在模板中也可以使用if语句,if 使用方法很简单,只需要在template中添加{{if}}, 如果有else,也一样,添加{{else}}。Template中代码如下:
 {{#each people}}      
   <div class="person">
     <p>{{title}}          
    {{#if author}}         //people数组中的每一项,如果有author属性,就进入if语句,显示以下html
            {{author.first_name}} {{author.last_name}}</p>
       {{else}}     //没有就显示以下html
            Unknown Author</p>
       {{/if}}
  </div>
{{/each}}

javascript模板引擎恰恰就是为了帮助我们有效的组织数据及其展示内容而出现的。和其它的模板使用方式一样,你需要做如下两个事情:
1. 创建展示模板      var myTemplate = Handlebars.compile($("#table-template").html());  $("#table-template").html()为模板内容

2. 将数据解析到模板中     $('#tableList').html(myTemplate(data));    myTemplate(data)为模板和数据生成的html

我们可以使用with块去定位我们需要的celebrity属性:

如果我们有一个这样的上下文对象:

var shoesData = {groupName:"Celebrities", celebrity:{firstName:"Mike", lastName:"Alexander" } };



<script id="shoe-template" type="x-handlebars-template">
  {{groupName}} Group
    {{#with celebrity}}    //进入到celebrity的上下文
      <li>{{firstName}} {{lastName}}</li>
    {{/with}}
</script>

下面的代码表明了怎样在Handlebars模板中添加注释:

{{! 在这其中的注释表达式不会被输出 }}
你也可使使用一般的HTML注释,但是它们会被输出到HTML页面源文件中,就像一般的HTML注释一样:

<!-- Regular HTML comments will be in the output -->

Handlebars可以使用../来查询当前上下文中的父路径的属性。比如,有一个数据对象如下:

var shoesData = {groupName:"Celebrities", users:[{name:{firstName:"Mike", lastName:"Alexander" }}, {name:{firstName:"John", lastName:"Waters" }} ]};

 
我们可以使用父路径 ../ 来得到groupName属性:

<script id="shoe-template" type="x-handlebars-template">
  {{#users}}     //此种方法也会把users数组中的每一项也输出
    <li>{{name.firstName}} {{name.lastName}} is in the {{../groupName}} group.</li>   //父路径下的groupName属性
  {{/users}}
</script>

unless辅助函数可以增强if,else。下面的代码意思:只有当userLoggedIn属性被检查为假值是其中的内容才会被渲染:

{{#unless userLoggedIn}} Please Log in. {{/unless}}


最后讲一下Handlebars最重要的使用方法:

Handlebars.js自定义辅助函数

Handlebars允许我们添加我们自己的自定义辅助函数,有了自定义辅助函数,我们可以添加任意的Javascript逻辑。我们需要在所有的Handlebars JS代码之前注册自定义辅助函数。自定义辅助函数在Javascript代码中被创建,而不是在Handlebars模板中。

你可以创建两种自定义辅助函数:自定义辅助函数(function helper),它不要使用块表达式就能运行,自定义块辅助函数,它需要和一个块表达式一起运行。

自定义函数辅助函数(function helper)

首先,我们必须用Handlebars.registerHelper方法注册一个自定义辅助函数。这个方法接收一个字符串(辅助函数的名字)作为第一个参数,一个具有任意参数个数的函数作为第二个参数。

Handlebars.registerHelper ("theNameOfTheHelper", function (theScore) {

  if (theScore >= 90) {
    return "A" ;
  }
  else if (theScore >= 80 && theScore < 90) {
    return "B" ;
  }
  else if (theScore >= 70 && theScore < 80) {
    return "C" ;
  }
  else {
    return "D" ;
  }

});

下面是一个使用我们刚才创建的自定义函数辅助函数的Handlebars模板:

<script id="shoe-template" type="x-handlebars-template">
  {{#theNameOfTheHelper score}}
</script>

下面是数据var contextObj = {score:85, userName:"Mike"};

最后,把score=85,传入到自定义函数中,返回B。于是模板最终结果返回一个"B"。

自定义块辅助函数

当我们注册了一个自定义块辅助函数时,Handlebars自动在回调函数中添加了一个可选择对象作为最后一个参数。这个可选择对象拥有一个fn方法,一个hash对象,以及一个inverse方法。fn方法接收一个对象(你的数据dataObject[i])作为自定义块表达式模板(<div>{{firstName}} {{lastName}}, Your Total Score is <strong>{{score}}</strong> </div>)中的上下文。你也可以传递任何数据对象,或者如果你想使用引用模板同样的上下文,你可以使用this(dataObject也就是contextObj)。

我们使用Handlebars.registerHelper注册一个userScore的块辅助函数。注意到参数中的最后一个项目是可选择对象,它由Handlebars自动添加:

Handlebars.registerHelper ("userScore", function (dataObject, options) {
  var templateWithInterpolatedData = "";

  for (var i = dataObject.length - 1; i >= 0; i--) {     //遍历dataObject数组
    dataObject[i].score = dataObject[i].score.reduce(function (prev, cur, index, array) {
      return prev + cur;      //数组中的每一项的score属性是一个数组,把这个数组的每一项相加,结果返回给score属性
    });  

    //这里我先介绍一下数组的reduce方法:reduce方法接受两个参数,一个回调方法,一个初始值。callback回调方法接受4个参数:之前值、当前值、索引值以及数组本身。initialValue(初始值)参数可选,表示初始值。若指定,则当作最初使用的previous值;如果缺省,则使用数组的第一个元素作为previous初始值,同时current往后排一位,相比有initialValue值少一次迭代。reduce方法,会把数组的每项进行迭代,最终的结果就是最后return的值。比如:var sum = [1, 2, 3, 4].reduce(function (previous, current, index, array) {return previous + current;});因为没有初始值,所以previous就是数组的第一项,current就是数组的第二项,index值就是当前值的index(当前是1),array就是原数组[1,2,3,4]。第一次返回1+2=3,第二次previous等于上一次返回的值3,current等于当前值3,返回6,第三次previous等于6,当前值4,返回10.这时数组循环结束,把最后的返回结果10,返回给sum(数组调用reduce的结果)。 最终得到的结果就是数组的总和。

    // dataObject[i]变成了{firstName: "Kapil", lastName:"Manish", score:201}

    templateWithInterpolatedData += options.fn (c);

    //将会把对象的数据插入到模板中,也就是把{firstName: "Kapil", lastName:"Manish", score:201}插入到:<div>{{firstName}} {{lastName}}, Your Total Score is <strong>{{score}}</strong> </div>,最后叠加成一个字符串

    

  }

  return templateWithInterpolatedData;     //把所有的数据对象插入到模板后生成的html字符串返回。
});

数据:var contextObj = [{firstName: "Kapil", lastName:"Manish", score:[22, 34, 45, 67]}, {firstName: "Bruce", lastName:"Kasparov", score:[10, 34, 67, 90]}];

模板:

<script id="shoe-template" type="x-handlebars-template">
  {{#userScore this}}     //执行userScore(contextObj)
    <div>{{firstName}} {{lastName}}, Your Total Score is <strong>{{score}}</strong> </div>
  {{/userScore}}
</script>

最终的结果:

HTML的输出结果是:

Bruce Kasparov, Your Total Score is 201

Kapil Manish, Your Total Score is 168

options.inverse方法:

inverse方法在任意块表达式总被当做else部分来使用。因此,当回调函数中的表达式为一个真值是你可以使用options.fn来返回。但是当回调函数中的表达式为假值时你可以使用options.inverse(去渲染else部分中的内容)。

options.hash对象:

Handlebars表达式不接收任何字符串和变量作为参数,但是你依然可以传递用空格分开的键-值对。例如:

(注意到这里没有逗号来分开键-值对变量,是空格)

{{#myNewHelper score=30 firstName="Jhonny" lastName="Marco"}}
  Show your HTML content here.
{{/myNewHelper}}
调用拥有键-值对作为参数的Handlebars表达式将会自动添加到辅助函数回调函数的options.hash对象上。因此:

Handlebars.registerHelper ("myNewHelper", function (dataObject, options) {
  //JSON.stringify用于序列化一个json对象为一个字符串
  console.log(JSON.stringify (options.hash));
  //输出结果为:{score:30, firstName:"Jhonny", lastName:"Marco"}

});

加油!

上一篇:【锋利的jQuery】学习笔记02


下一篇:DRF 缓存解决方案 drf-extensions / redis