转自https://segmentfault.com/a/1190000002796397

概述

刚接触前端的时候,师傅就给我推荐了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 += '<p>' + item.name + ':' + item.age + '</p>';
});$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>isEdit</p>
{{/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>isEdit</p>
{{else}}<p>isNotEdit</p>
{{/if}}   #数据
var data = {isEdit: false
};#页面效果
isNotEdit

看看多分支是咋子写的

#template
{{#if isEdit}}<p>isEdit</p>
{{else if isRead}}<p>isNotEdit isRead</p>
{{else}}<p>isNotRead</p>
{{/if}}   #数据
var data = {isEdit: false,isRead: false
};#页面效果
isNotRead

unless

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

#template
{{#unless isEdit}}<p>isNotEdit</p>
{{else unless isRead}}<p>isRead</p>
{{else}}<p>isNotRead</p>
{{/unless}}   #数据
var data = {isEdit: true,isRead:true
};#页面效果
isNotRead

each

遍历数组

#template
{{#each this}}<p>{{this.name}}:{{this.age}}</p>
{{else}}<p>no data</p>
{{/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 text</div>'
};#页面效果
<div>this is rich text</div>

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

{{{richText}}}

Helpers

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

#template
{{#each this}}<p>{{name}}:{{addTime}}</p>
{{/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}}<p>{{name}}:{{moment addTime}}</p>
{{/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
<p>{{query name 'arg2' hash1='hash1' hash2='hash2'}}</p>#数据
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
<p>{{safe}}</p>#js
Handlebars.registerHelper('safe', function () {return new Handlebars.SafeString('<div>safe string</div>')
});#页面效果
safe string

Partials

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

#template
<p>{{> footer}}</p>#js
Handlebars.registerPartial('footer', function () {return new Handlebars.SafeString('<div>This is footer</div>')
});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.js入门教程相关推荐

  1. Vue.js入门教程-组件注册

    一.组件创建 1.1 创建步骤 创建Vue的组件都有三个基本步骤是 [①创建组件构造器.②注册组件和③使用组件]. 1.2 基本示例 比如,我们创建一个Button组件. // 1. 创建一个组件构造 ...

  2. js模版引擎handlebars.js实用教程——结束语

    返回目录 有了这些功能,[ajax json Handlebars]替代[vo el表达式]不成问题,新时代的曙光已经来临,最佳解决方案在此,您还等什么? 教程到此结束...祝读者学习愉快... 转载 ...

  3. js读取http chunk流_极简 Node.js入门 教程双工流

    点击上方蓝字关注我们 小编提示: 本文是由 ICBU 的谦行小哥哥出品,我们会持续发出极简 Node.js入门 教程,敬请期待哦,文中有比较多的演示代码建议横屏阅读 双工流就是同时实现了 Readab ...

  4. Node.js 入门教程 23 使用 npm 的语义版本控制 24 卸载 npm 软件包 25 npm 全局或本地的软件包

    Node.js 入门教程 Node.js官方入门教程 Node.js中文网 本文仅用于学习记录,不存在任何商业用途,如侵删 文章目录 Node.js 入门教程 23 使用 npm 的语义版本控制 24 ...

  5. Vue.js入门教程(适合初学者)

    Vue.js入门教程 Vue官网网址:Vue.js 中文网 Vue.js Vue.js是渐进式JavaScript 框架,是一套构建用户界面的渐进式框架.也可以说Vue.js 是一个用来构建网页界面的 ...

  6. Node.js 入门教程 6 V8 JavaScript 引擎

    Node.js 入门教程 Node.js官方入门教程 Node.js中文网 本文仅用于学习记录,不存在任何商业用途,如侵删 文章目录 Node.js 入门教程 6 V8 JavaScript 引擎 6 ...

  7. Ember.js入门教程、博文汇总

    第一章 对象模型 Ember.js 入门指南--类的定义.初始化.继承 Ember.js 入门指南--类的扩展(reopen) Ember.js 入门指南--计算属性(compute properti ...

  8. egg.js入门教程视频文件(转载于cnode社区)

    记得上篇博客我满怀欣喜的去搞富文本,结果撞的头破血流. 简直是惨不忍睹.后来我也说了,我的那个有比较严重的问题,后期会考虑重构.(第一版已经放弃了) 之后我说我会去看关于后端nodejs koa框架方 ...

  9. Two.js入门教程

    项目中需要前端画svg图像,直接在html上写标签不太优雅,于是找到了Two.js这个第三方类库,使用其完成了开发任务后,分享下使用心得,就算是入门教程了. 其官方网站为https://two.js. ...

最新文章

  1. jemeter多场景混合案例_Jmeter多业务混合场景如何设置各业务所占并发比例
  2. Mysql之一:mysqldump和LVM逻辑卷快照
  3. JZOJ 1637. 【ZJOI2009】狼和羊的故事
  4. win7安装git客户端和简单配置
  5. sql和sqlite常用查询语句
  6. 总结2:上传图片至指定服务器
  7. JBoss5开发web service常见问题
  8. VS2008 Qt Designer 中自定义信号槽
  9. 选择更安全的方式注册你的puppet节点
  10. 分享400多道算法题,来挑战吧
  11. mysql 索引使用不当_MySQL笔记:select默认使用不当索引导致的巨大性能损失问题_MySQL...
  12. 电脑更新重启后黑屏_电脑黑屏重启还是黑屏的解决方法教程
  13. C/C++语言函数之strlen函数用法
  14. java 1.5 jdk_jdk1.5安装及配置
  15. java如何实现对word设置只读或者加密
  16. 微信聊天记录如何恢复
  17. 星际争霸2-数据编辑器-菜鸟入门
  18. PM应该了解的九大项目管理问题
  19. 安卓之父安迪·鲁宾:让乔布斯羡慕嫉妒恨的人
  20. js提示“未结束的字符串常量”

热门文章

  1. matlab图像区域填充的原理_MATLAB中怎么用fill填充指定区域?
  2. php where 时间条件,thinkphp5日期时间查询比较和whereTime使用方法
  3. SAP部分清账与剩余清账
  4. 【FI】SAP 付款及清账
  5. 超越Framer的基础知识
  6. vue项目中如何设置ico图标
  7. Thoth-Tech靶机实验实战演练
  8. 推挽输出、开漏输出和悬空输入等
  9. 敏捷开发:一文了解影响地图和用户故事地图之间的那些事儿
  10. apache的ab压力测试介绍