使用Handlebars,你能够轻松建立语义化模板,Mustache模板和Handlebars是兼容的,因此你能够将Mustache导入Handlebars以使用 Handlebars 强大的功能。javascript
开始
Handlebars模板看起来和HTML同样,只是嵌入了 handlebars 表达式php
- <div class="entry">
- <h1>{{title}}</h1>
- <div class="body">
- {{body}}
- </div>
- </div>
handlebars表达式以{{
开头,中间写一些内容,以}}
结尾。css
你可使用<script>
标签引入handlebars模板:html
- <script id="entry-template" type="text/x-handlebars-template">
- template content
- </script>
在javascript中使用Handlebars.compile
编译模板:前端
- var source = $("#entry-template").html();
- var template = Handlebars.compile(source);
你也能够预编译你的模板,而后只需引入更小的运行时库(handlebars.runtime.js),避免在浏览器中编译,提升性能,这在移动设备中显得更重要。html5
传入数据上下文(context),handlebars会执行并生成HTML:java
- var context = {title: "My New Post", body: "This is my first post!"}
- var html = template(context);
获得的结果是:node
- <div class="entry">
- <h1>My New Post</h1>
- <div class="body">
- This is my first post!
- </div>
- </div>
HTML编码
在handlebars里,{{expression}}
会返回一个通过编码的HTML,若是你不但愿被编码,可使用{{{
python
- <div class="entry">
- <h1>{{title}}</h1>
- <div class="body">
- {{{body}}}
- </div>
- </div>
使用这样的数据上下文:react
- {
- title: "All about <p> Tags",
- body: "<p>This is a post about <p> tags</p>"
- }
结果是:
- <div class="entry">
- <h1>All About <p> Tags</h1>
- <div class="body">
- <p>This is a post about <p> tags</p>
- </div>
- </div>
handlebars不会编码Handlebars.SafeString
。若是你自定义一个helper,返回一段HTML代码,你须要返回new Handlebars.SafeString(result)
。此时,你须要手动对内容进行编码:
- Handlebars.registerHelper('link', function(text, url) {
- text = Handlebars.Utils.escapeExpression(text);
- url = Handlebars.Utils.escapeExpression(url);
- var result = '<a href="' + url + '">' + text + '</a>';
- return new Handlebars.SafeString(result);
- });
这里将会对传入的参数进行编码,返回值是“安全的”,因此就算你不使用{{{
,handlebars也不会再次编码了。
块表达式
块表达式容许你定义helper,用不一样的数据上下文(context)调用一段模板。下面咱们定义一个生成列表的helper:
- {{#list people}}{{firstName}} {{lastName}}{{/list}}
若是咱们的数据是这样的:
- {
- people: [
- {firstName: "Yehuda", lastName: "Katz"},
- {firstName: "Carl", lastName: "Lerche"},
- {firstName: "Alan", lastName: "Johnson"}
- ]
- }
咱们建立一个叫list
的helper来生成列表,helper接受people
做为第一个参数,一个option对象(hash)做为第二个参数。option包含一个属性fn
,他能够调用一个context就像普通模板同样。
- Handlebars.registerHelper('list', function(items, options) {
- var out = "<ul>";
- for(var i=0, l=items.length; i<l; i++) {
- out = out + "<li>" + options.fn(items[i]) + "</li>";
- }
- return out + "</ul>";
- });
执行后,获得:
- <ul>
- <li>Yehuda Katz</li>
- <li>Carl Lerche</li>
- <li>Alan Johnson</li>
- </ul>
块表达式有不少特性,例如,能够建立一个else
块(内置的if helper就有else块)。另外,若是options.fn(context)
对内容编码过了,handlebars就不会helper内容进行编码了,不然就编码两次了。
Handlebars 路径
Handlebars支持简单的路径
- <p>{{name}}</p>
也支持嵌套路径,能够查找下一级的属性
- <div class="entry">
- <h1>{{title}}</h1>
- <h2>By {{author.name}}</h2>
- <div class="body">
- {{body}}
- </div>
- </div>
此模板使用下面的数据:
- var context = {
- title: "My First Blog Post!",
- author: {
- id: 47,
- name: "Yehuda Katz"
- },
- body: "My first post. Wheeeee!"
- };
嵌套路径一样支持../,
- <h1>Comments</h1>
- <div id="comments">
- {{#each comments}}
- <h2><a href="/posts/{{../permalink}}#{{id}}">{{title}}</a></h2>
- <div>{{body}}</div>
- {{/each}}
- </div>
尽管连接在打印出的时候,是以comments为上下文的,可是它能够访问到上一级的上下文(context)找到permalink。
../引用上一级的做用域,直接看一下上面模板对应的数据就明白了
- var data = {
- permalink:'http://keenwon.com',
- comments: [
- {id:1,title:'连接1',body:'连接1'},
- {id:2,title:'连接2',body:'连接2'}
- ]
- };
Handlebars能够经过this
引用解决helpers和数据命名冲突的问题。
- <p>{{./name}} or {{this/name}} or {{this.name}}</p>
模板注释 {{! }}
or {{!-- --}}
你能够在 handlebars 里加注释:
- <div class="entry">
- {{! only output this author names if an author exists }}
- {{#if author}}
- <h1>{{firstName}} {{lastName}}</h1>
- {{/if}}
- </div>
注释不会出如今输出结果里,若是想要显示出来,可使用html的注释(会被执行,而后以注释的形式显示,因此若是html注释内有错,仍是会报错)
- <div class="entry">
- {{! 这个注释不会显示在输出结果中 }}
- <!-- 会显示 -->
- </div>
全部注释必须包含结束标签}}
,多行注释可使用{{!-- --}}
Helpers
Handlebars Helpers能够读取到模板中的任何数据上下文,你可使用Handlebars.registerHelper
注册一个helpers。
- <div class="post">
- <h1>By {{fullName author}}</h1>
- <div class="body">{{body}}</div>
- <h1>Comments</h1>
- {{#each comments}}
- <h2>By {{fullName author}}</h2>
- <div class="body">{{body}}</div>
- {{/each}}
- </div>
而后使用以下的数据上下文和Helpers:
- var context = {
- author: {firstName: "Alan", lastName: "Johnson"},
- body: "I Love Handlebars",
- comments: [{
- author: {firstName: "Yehuda", lastName: "Katz"},
- body: "Me too!"
- }]
- };
- Handlebars.registerHelper('fullName', function(person) {
- return person.firstName + " " + person.lastName;
- });
结果是:
- <div class="post">
- <h1>By Alan Johnson</h1>
- <div class="body">I Love Handlebars</div>
- <h1>Comments</h1>
- <h2>By Yehuda Katz</h2>
- <div class="body">Me Too!</div>
- </div>
使用this
能够访问到当前的上下文
- <ul>
- {{#each items}}
- <li>{{agree_button}}</li>
- {{/each}}
- </ul>
使用以下的Helpers和数据上下文
- var context = {
- items: [
- {name: "Handlebars", emotion: "love"},
- {name: "Mustache", emotion: "enjoy"},
- {name: "Ember", emotion: "want to learn"}
- ]
- };
- Handlebars.registerHelper('agree_button', function() {
- return new Handlebars.SafeString(
- "<button>I agree. I " + this.emotion + " " + this.name + "</button>"
- );
- });
结果是:
- <ul>
- <li><button>I agree. I love Handlebars</button></li>
- <li><button>I agree. I enjoy Mustache</button></li>
- <li><button>I agree. I want to learn Ember</button></li>
- </ul>
若是你的helpers返回一个html片断,不想被编码。必须new一个Handlebars.SafeString
返回出来。
内置的Helpers
The with
Block Helper
一般,Handlebars会将数据上下文传入编译方法:
- var source = "<p>{{lastName}}, {{firstName}}</p>";
- var template = Handlebars.compile(source);
- template({firstName: "Alan", lastName: "Johnson"});
结果:
- <p>Johnson, Alan</p>
使用with
能够改变当前的上下文
- <div class="entry">
- <h1>{{title}}</h1>
- {{#with author}}
- <h2>By {{firstName}} {{lastName}}</h2>
- {{/with}}
- </div>
数据上下文以下:
- {
- title: "My first post!",
- author: {
- firstName: "Charles",
- lastName: "Jolley"
- }
- }
结果:
- <div class="entry">
- <h1>My first post!</h1>
- <h2>By Charles Jolley</h2>
- </div>
The each
block helper
你可使用内置的each
helper生成列表,可使用this
访问当前项。
- <ul class="people_list">
- {{#each people}}
- <li>{{this}}</li>
- {{/each}}
- </ul>
数据上下文以下:
- {
- people: [
- "Yehuda Katz",
- "Alan Johnson",
- "Charles Jolley"
- ]
- }
结果:
- <ul class="people_list">
- <li>Yehuda Katz</li>
- <li>Alan Johnson</li>
- <li>Charles Jolley</li>
- </ul>
你能够在任何上下文里,使用this
引用当前的上下文
另外,还可使用{{else}}
块,当列表内容为空的时候会显示{{else}}
的内容
- {{#each paragraphs}}
- <p>{{this}}</p>
- {{else}}
- <p class="empty">暂无内容</p>
- {{/each}}
在each
中循环每一项的时候,可使用{{@index}}
获取当前的序号。
- {{#each array}}
- {{@index}}: {{this}}
- {{/each}}
对于object,可使用{{key}}
获取当前的key。
- {{#each object}}
- {{@key}}: {{this}}
- {{/each}}
在迭代的过程当中,可使用@first
和@last
判断当前的第一步和最后一步,对于object,只有@first
可用。
The if
block helper
可使用if
helper有条件的渲染block,若是是false
, undefined
, null
, ""
或者 []
(a “falsy” value),Handlebars不会渲染此block.
- <div class="entry">
- {{#if author}}
- <h1>{{firstName}} {{lastName}}</h1>
- {{/if}}
- </div>
若是使用的是空的数据上下文(例如{}
),author会返回undefined
,结果是:
- <div class="entry">
- </div>
当使用块表达式,可使用{{else}}来指定一个“片断”,当结果是 falsy value 的时候呈现
- <div class="entry">
- {{#if author}}
- <h1>{{firstName}} {{lastName}}</h1>
- {{else}}
- <h1>Unknown Author</h1>
- {{/if}}
- </div>
The unless
block helper
unless
的做用和if
恰好相反,但表达式返回falsy value的时候渲染block
- <div class="entry">
- {{#unless license}}
- <h3 class="warning">WARNING: This entry does not have a license!</h3>
- {{/unless}}
- </div>
若是当前上下文的license
返回一个falsy value,Handlebars会输出警告信息,不然什么都不输出。
The log
block helper
log
helper容许执行模板的时候输出当前上下文的状态
- {{log "Look at me!"}}
信息传给Handlebars.logger.log
,重写这个函数能够实现自定义的log。
原文:Learn Handlebars in 10 Minutes or Less
翻译:前端开发whqet, 意译为主,不当之处敬请指正。
做者简介:Danny Markov ,Tutorialzine的bootstrap和html5方面的专家,业余时间喜欢骑自行车或在公园的某个角度码码。
译者的话:听说handlebars是一个流行的模板引擎,能够把html从javascript中分离出来,写更清晰的代码。来不妨一试。
Handlebars.js是一个很是流行的功能强大的模板引擎,简单易用,具有较好的学习社区。它基于 Mustache 模板引擎,而且作了诸多改进。利用Handlebars您能够方便的把html从javascript代码中分离出来,从而书写更清晰的代码。
本文章试图经过十分钟左右的时间带您领略Handlebars的风采,刚开始学的时候可能费点周折,可是您一旦上手,一切将变得很简单。
0.引入项目
在项目中引入Handlebars很是简单,到 http://handlebarsjs.com/下载最新版本(本文写做时,最新版为2.0.0),而后使用script标签引入便可。固然您也可使用cdn的方式,享受cdn方式的畅快。如代码所示。
- // From File
- <script src="handlebars-v2.0.0.js"></script>
- // From CDN
- <script src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/2.0.0/handlebars.js"></script>
1.Templates
当您引入库以后,咱们能够愉快的书写模板了,推荐的方式是经过特殊type的script标签来添加模板,type属性是很是重要的,不然浏览器会将它们看作javascrip解析。
模板具备一个很容易理解的语法,可使用html、普通文本和表达式,表达式一般被包含在两对或三对花括号里,能够包含变量或功能函数。模板须要编译以后才能使用,以下面代码所示,案例我放在了codepen,你们不妨一看。。注意一点,咱们使用了jquery仅仅为了方便dom操做,handlebars能够脱离jquery良好工做。
- <!--模板. -->
- <!--须要数据的地方,用{{}}括起来.-->
- <script id="address-template" type="text/x-handlebars-template">
- <p>You can find me in {{city}}. My address is {{number}} {{street}}.</p>
- </script>
- <!--新的内容在这里展现-->
- <div class="content-placeholder"></div>
- $(function () {
- // 抓取模板数据
- var theTemplateScript = $("#address-template").html();
- // 编译模板
- var theTemplate = Handlebars.compile(theTemplateScript);
- // 定义数据
- var context={
- "city": "London",
- "street": "Baker Street",
- "number": "221B"
- };
- // 把数据传送到模板
- var theCompiledHtml = theTemplate(context);
- // 更新到模板
- $('.content-placeholder').html(theCompiledHtml);
- });
2. Expressions
上面所示的案例,表达式中的任何html代码将会被自动忽略,这是一个很是实用的性能,可是有的时候咱们须要解析html,那么就要用三个花括号{{{ }}},以下面代码所示,演示效果在codepen。
另外,handlebars表达式容许嵌套值,能够方便咱们访问javascript对象的任何值。
- <script id="expressions-template" type="text/x-handlebars-template">
- {{description.escaped}}
- {{example}}
- <br><br>
- {{description.unescaped}}
- {{{example}}}
- </script>
- <div class="content-placeholder"></div>
- $(function () {
- // <span style="font-family:Arial, Helvetica, sans-serif;">抓取模板数据</span>
- var theTemplateScript = $("#expressions-template").html();
- // 编译模板
- var theTemplate = Handlebars.compile(theTemplateScript);
- // 定义数据
- var context={
- "description": {
- "escaped": "Using {{}} brackets will result in escaped HTML:",
- "unescaped": "Using {{{}}} will leave the context as it is:"
- },
- "example": "<button> Hello </button>"
- };
- // 传送数据
- var theCompiledHtml = theTemplate(context);
- // 展现到页面
- $('.content-placeholder').html(theCompiledHtml);
- });
3. Context
Handlebars利用了Mustache的强大特性,context就是其中之一。咱们能够把须要传递的数据放在这个javascript对象中,使用#each、#with等方法能够方便的使用该对象的数据。看了下面这个案例,那就明白了,演示效果在codepen。
- <!-- #each能够遍历数据. -->
- <script id="example-template" type="text/x-handlebars-template">
- <!-- 遍历people -->
- {{#each people}}
- <!-- 直接使用每一个people的数据 -->
- <p>{{firstName}} {{lastName}}</p>
- {{/each}}
- </script>
- $(function () {
- var theTemplateScript = $("#example-template").html();
- var theTemplate = Handlebars.compile(theTemplateScript);
- var context = {
- people: [
- { firstName: 'Homer', lastName: 'Simpson' },
- { firstName: 'Peter', lastName: 'Griffin' },
- { firstName: 'Eric', lastName: 'Cartman' },
- { firstName: 'Kenny', lastName: 'McCormick' },
- { firstName: 'Bart', lastName: 'Simpson' }
- ]
- };
- var theCompiledHtml = theTemplate(context);
- $(document.body).append(theCompiledHtml);
- });
4. Helpers
Handlebars不容许在模板中使用javascript,而是提供了一些列的功能函数(helpers),能够在模板中调用,方便代码重用和创造复杂模板。使用表达式调用helpers的格式相似如此,{{helpername}},同时也能够传递参数,{{helpname 12345}}。
开发新的helper,使用registerHelper function,下面代码演示了如何建立新的功能函数,如何使用内置的功能函数,演示文件在codepen。
- <script id="built-in-helpers-template" type="text/x-handlebars-template">
- {{#each animals}}
- <p>
- The {{capitalize this.name}} says
- {{#if this.noise}}
- "{{this.noise}}".
- {{else}}
- nothing since its a {{this.name}}.
- {{/if}}
- </p>
- {{/each}}
- </script>
- <div class="content-placeholder"></div>
- $(function () {
- // 定义a helper
- Handlebars.registerHelper('capitalize', function(str){
- // str is the argument passed to the helper when called
- str = str || '';
- return str.slice(0,1).toUpperCase() + str.slice(1);
- });
- var theTemplateScript = $("#built-in-helpers-template").html();
- var theTemplate = Handlebars.compile(theTemplateScript);
- var context = {
- animals:[
- {
- name: "cow",
- noise: "moooo"
- },
- {
- name: "cat",
- noise: "meow"
- },
- {
- name: "fish",
- noise: ""
- },
- {
- name: "farmer",
- noise: "Get off my property!"
- }
- ]
- };
- var theCompiledHtml = theTemplate(context);
- $('.content-placeholder').html(theCompiledHtml);
- });
5. Block helpers
Block helpers像普通的功能函数同样,可是有开始和结束标签(相似于内置的#if、#each等),能够修改包含的html的内容。建立更为复杂一些,当时功能更增强大。常用它们重复使用功能、建立一大段可重用的html等。
一样使用Handlebars.registerHelper()建立block helper,不一样的是咱们须要使用第二参数,回调函数。看看下面的代码,体会强大功能。
- <script id="block-expressions-template" type="text/x-handlebars-template">
- <p> The <b> {{#uppercase}} konami {{/uppercase}} </b> Code is a cheat code that appears in many video games.</p>
- <p>During the title screen before the game demo begins, the player could press the following sequence of buttons on the game controller to enable the cheat:</p>
- <p>{{#uppercase}}{{code}}{{/uppercase}}</p>
- <p>The code is also present as an Easter egg on a number of websites.</p>
- </script>
- <div class="content-placeholder"></div>
- $(function () {
- var theTemplateScript = $("#block-expressions-template").html();
- // This is our block helper
- // The name of our helper is provided as the first parameter - in this case 'uppercase'
- Handlebars.registerHelper('uppercase', function(options) {
- // "this" is the context that existed when calling the helper.
- // The options object has a special function - fn. This is a
- // compiled version of the template that is contained between the opening and closing
- // blocks of this helper. To get a string, call fn with the context:
- return options.fn(this).toUpperCase();
- });
- var theTemplate = Handlebars.compile(theTemplateScript);
- var context = {
- "code": "up up down down left right left right b a select start"
- };
- var theCompiledHtml = theTemplate(context);
- $('.content-placeholder').html(theCompiledHtml);
- });
6.资源和延伸阅读
如今你基本上了解了handlebars的经常使用功能,一样再多学点也问题不大,您能够经过如下资源深刻学习。
Handlebars.js-官方网站,能够获取更多案例、官方文档
Try Handlebars.js-尝试不一样的应用情境(基于老版本)
Handlebars Helpers-handlebars helpers集
SWAG-更多
Handlebars API Reference -api文档
Handlebars
让你可以有能力高效地容易地创立语义化的模版。Handlebars
兼容Mustache
语法,在大多数状况下它能够读取Mustache
的语法并在你当前模板中使用。具体点击这里
安装
- 下载
npm install --save handlebars
bower install --save handlebars
开始使用
Handlebars
模板看起来就像嵌套handlebars
表达式的规范的HTML。
<div class="entry"> <h1>{{title}}</h1> <div class="body"> {{body}} </div> </div>
handlebars
表达式: {{ cnt }}
你也能够经过<script>
标签包裹handlebars
表达式传递模板给浏览器:
<script id="entry-template" type="text/x-handlebars-template"> <div class="entry"> <h1>{{title}}
你必须把模板放在
<script>
标签里,这很重要。不要直接把它放在HTML中不然HTML的解析会改变模板内容。
在JavaScript
中,使用Handlebars.compile
来编译模板:
var source = $("#entry-template").html(); var template = Handlebars.compile(source); // ‘entry-template’就是包裹模板的script的id
注意这种方法在产品应用中不推荐使用。更好的方法是预编译你的模版。这将使要求的运行库更小,模板没必要在浏览器中编译,显著地节省了时间。这在移动设备上尤其重要。
在JavaScript中,使用Handlebars.compile()方法来预编译模板 例如:(这是一套规则)
//用jquery获取模板 var tpl = $("#tpl").html(); //原生方法 var source = document.getElementById('#tpl').innerHTML; //预编译模板 var template = Handlebars.compile(source); //模拟json数据 var context = { name: "zhaoshuai", content: "learn Handlebars"}; //匹配json内容 var html = template(context); //输入模板 $(body).html(html);
经过解析context
处理handlebars模板
获取HTML内容
:
var context = {title: "My New Post", body: "This is my first post!"}; var html = template(context);
输出html:
<div class="entry"> <h1>My New Post</h1> <div class="body"> This is my first post! </div> </div>
HTML转码
Handlebars
的转码HTML值经过{{expression}}
返回. 若是你不想handlebars转码一个值的话,使用{{{expression}}}
<div class="entry"> <h1>{{title}}</h1> <div class="body"> {{{body}}} </div> </div>
上下文内容:
{
title: "All about <p> Tags", body: "<p>This is a post about <p> tags</p>" }
输出:
<div class="entry"> <h1>All About <p> Tags</h1> <div class="body"> <p>This is a post about <p> tags</p> </div> </div>
Handlebars
不会转义 Handlebars.SafeString
. 若是你写了输出自己所含HTML的辅助 helper
, 你其实想返回一个新的Handlebars.SafeString
.在这种状况下,你想手动拼接参数.
Handlebars.registerHelper('link', function(text, url) { text = Handlebars.Utils.escapeExpression(text); url = Handlebars.Utils.escapeExpression(url); var result = '<a href="' + url + '">' + text + '</a>'; return new Handlebars.SafeString(result); });
这样能够避免字符串被转码,正确响应参数,即便你不适用{{{
也不会被转码。
块级表达式
块级表达式 容许你定义一个能够触发一个与当前不一样的上下文来替换模板的相应内容的helper。这些块级辅助helper经过在helper的名字前加#
并在结束时名字前加/
:
{{#list people}}{{firstName}} {{lastName}}{{/list}}
渲染context:
{
people: [ {firstName: "Yehuda", lastName: "Katz"}, {firstName: "Carl", lastName: "Lerche"}, {firstName: "Alan", lastName: "Johnson"} ] }
咱们会建立一个叫list
的helper输出HTML列表。该列表以people
为第一个参数,哈希选项为第二个参数。这些选项里包含一条名为fn的属性,在handlebars模板中经过这些属性名获取值
Handlebars.registerHelper('list', function(items, options) { var out = "<ul>"; for(var i=0, l=items.length; i<l; i++) { out = out + "<li>" + options.fn(items[i]) + "</li>"; } return out + "</ul>"; });
渲染结果:
<ul> <li>Yehuda Katz</li> <li>Carl Lerche</li> <li>Alan Johnson</li> </ul>
块级辅助helper有不少特色,例如能够建立一个else
部分.由于当你调用options.fn(context)
时块级helper的内容已经被转码过,因此handlebars不会再去转码helper的内容。
handler 的路径
Handlebars
支持简单的路径,就像 Mustache
.
<p>{{name}}</p>
Handlebars 也支持嵌套的属性,好比对象的属性.
<div class="entry"> <h1>{{title}}</h1> <h2>By {{author.name}}</h2> <div class="body"> {{body}} </div> </div>
模板工做的对象context:
var context = {
title: "My First Blog Post!", author: { id: 47, name: "Yehuda Katz" }, body: "My first post. Wheeeee!" };
这使得使用handlebars
模板处理JSON
字符串成为可能。内嵌的handlebars的路径也包括../
语法,至关于当前路径的父级。
<h1>Comments</h1> <div id="comments"> {{#each comments}} <h2><a href="/posts/{{../permalink}}#{{id}}">{{title}}</a></h2> <div>{{body}}</div> {{/each}} </div> {{permalink}} {{#each comments}} {{../permalink}} {{#if title}} {{../permalink}} {{/if}} {{/each}}
这里例子中引用了相同的permalink
即便他们在不一样的块中。这种行为是新的,handlebars4支持。
Handlebars的内置块表达式(Block helper)
-
each block helper
你可使用内置的{{#each}}
helper遍历列表块内容,用this来引用遍历的元素 例如:<ul> {{#each name}} <li>{{this}}</li> {{/each}} </ul>
对应适用的json数据
{ name: ["html","css","javascript"] };
这里的this指的是数组里的每一项元素,和上面的Block很像,但原理是不同的这里的name是数组,而内置的each就是为了遍历数组用的,更复杂的数据也一样适用。
-
if else block helper
{{#if}}
就你使用JavaScript同样,你能够指定条件渲染DOM,若是它的参数返回false,undefined, null, "" 或者 [] (a "falsy" value)
, Handlebar将不会渲染DOM,若是存在{{#else}}
则执行{{#else}}
后面的渲染。{{#if list}} <ul id="list"> {{#each list}} <li>{{this}}</li> {{/each}} </ul> {{else}} <p>{{error}}</p> {{/if}}
对应适用json数据
var data = {info:['HTML5','CSS3',"WebGL"],"error":"数据取出错误"}
这里
{{#if}}
判断是否存在list数组,若是存在则遍历list,若是不存在输出错误信息 -
unless block helper
{{#unless}}这个语法是反向的if语法也就是当判断的值为false时他会渲染DOM 例如:{{#unless data}} <ul id="list"> {{#each list}} <li>{{this}}</li> {{/each}} </ul> {{else}} <p>{{error}}</p> {{/unless}}
-
with block helper
{{#with}}通常状况下,Handlebars模板会在编译的阶段的时候进行context传递和赋值。使用with的方法,咱们能够将context转移到数据的一个section里面(若是你的数据包含section)。 这个方法在操做复杂的template时候很是有用。<div class="entry"> <h1>{{title}}</h1> {{#with author}} <h2>By {{firstName}} {{lastName}}</h2> {{/with}} </div>
对应适用json数据
{ title: "My first post!", author: { firstName: "Charles", lastName: "Jolley" } }
Handlebar的注释(comments)
Handlebars也可使用注释写法以下
{{! handlebars comments }}
Handlebars的访问(Path)
Handlebar支持路径和mustache,Handlebar还支持嵌套的路径,使得可以查找嵌套低于当前上下文的属性
能够经过.来访问属性也可使用../,来访问父级属性。 例如:(使用.访问的例子)
<h1>{{author.id}}</h1>
对应json数据
{
title: "My First Blog Post!", author: { id: 47, name: "Yehuda Katz" }, body: "My first post. Wheeeee!" };
例如:(使用../访问)
{{#with person}} <h1>{{../company.name}}</h1> {{/with}}
对应适用json数据
{
"person": { "name": "Alan" }, company: {"name": "Rad, Inc." } };
自定义helper
Handlebars,能够从任何上下文能够访问在一个模板,你可使用Handlebars.registerHelper()方法来注册一个helper。
调试技巧
把下面一段"debug helper"加载到你的JavaScript代码里,而后在模板文件里经过{{debug}}或是{{debug someValue}}方便调试数据
Handlebars.registerHelper("debug", function(optionalValue) { console.log("Current Context"); console.log("===================="); console.log(this); if (optionalValue) { console.log("Value"); console.log("===================="); console.log(optionalValue); } });
handlebars的jquery插件
(function($) { var compiled = {}; $.fn.handlebars = function(template, data) { if (template instanceof jQuery) { template = $(template).html(); } compiled[template] = Handlebars.compile(template); this.html(compiled[template](data)); }; })(jQuery); $('#content').handlebars($('#template'), { name: "Alan" });
javascript/jquery模板引擎——Handlebars初体验
Handlebars.js下载地址:http://handlebarsjs.com/
最近本身在建一个站,采用彻底的先后端分离的方式,如今正在作前端的部分。其中有项功能是须要ajax调用后端接口,返回json数据后要动态的插入数据。可是一开始我本身是用最"传统"的经过js拼接html字符串,而后再用jq插入到页面中的方式。好比说下面的这个例子,我要显示一个我的信息卡:
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="utf-8"> 5 <title>Index</title> 6 <script src="js/jquery.js"></script> 7 <style> 8 .person { 9 font-size:40px; 10 float:left; 11 padding:20px; 12 margin:30px; 13 background-color:pink; 14 } 15 </style> 16 </head> 17 18 <body> 19 <div class = "person" id="person_info"> 20 </div> 21 </body> 22 23 <script> 24 var data = { 25 name:'约翰莫里森', 26 home:'美国', 27 job:'摔跤手' 28 }; 29 30 var str = ""; 31 str += "<div>姓名:" + data.name + "</div>"; 32 str += "<div>出生地:" + data.home + "</div>"; 33 str += "<div>职业:" + data.job + "</div>"; 34 35 $('#person_info').html(str); 36 </script> 37 </html>
这里我得用本身"手动"拼接html字符串,何况这只是一个极其简单的例子,若是标签之间的嵌套、属性复杂的话,这种方式写起来至关的麻烦,且标签间没有层次结构,可读性和维护性极差。
后来偶然在个地方了解到了模板引擎,今后这种工做一下简便了许多! 先看下用Handlebars完成上面的例子是如何操做的:
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="utf-8"> 5 <title>Handlebars</title> 6 <script src="js/jquery.js"></script> 7 <script src="js/handlebars.js"></script> 8 <style> 9 .person { 10 font-size:40px; 11 float:left; 12 padding:10px; 13 margin-left:30px; 14 background-color:pink; 15 } 16 </style> 17 </head> 18 19 <body> 20 <div id = "person_info"> 21 <div class = "person"> 22 <div>姓名:{{name}}</div> 23 <div>出生地:{{home}}</div> 24 <div>职业:{{job}}</div> 25 </div> 26 </div> 27 </body> 28 29 <script> 30 var data ={ 31 name:'约翰莫里森', 32 home:'美国', 33 job:'摔跤手' 34 }; 35 36 var tmpl = $('#person_info').html(); 37 38 var func = Handlebars.compile(tmpl); 39 40 var result = func(data); 41 42 $('#person_info').html(result); 43 </script> 44 45 </html>
先从最下面的js代码讲起,核心代码就这四句:
1 var tmpl = $('#person_info').html(); 2 var func = Handlebars.compile(tmpl); 3 var result = func(data); 4 $('#person_info').html(result);
第一行的 var tmpl = $('#person_info').html(); 就只是基本的jq语法;<body>中<div id="person_info">块中的就是html模板,这里取得了该div块下的html模板内容(对象tmpl)。此时若是用console.log(tmpl)打印该对象的话,获得的内容是
1 <div class = "person"> 2 <div>姓名:{{name}}</div> 3 <div>出生地:{{home}}</div> 4 <div>职业:{{job}}</div> 5 </div>
用两个大括号括起来的变量名会在后面 向函数传入数据(下面会提到) 时进行匹配。
第二行 var func = Handlebars.compile(tmpl); 经过Handlebars的compile()对刚刚取得的html模板进行预编译,返回的是一个函数(如今对象func即为该函数)。
var result = func(data); 这个函数带有一个参数,该参数为用以匹配模板用的数据。我这里事先写死一个数据对象data,而后传入该函数内。返回值是匹配好的html内容,打印出来就是 :
1 <div class = "person"> 2 <div>姓名:约翰莫里森</div> 3 <div>出生地:美国</div> 4 <div>职业:摔跤手</div> 5 </div>
最后用jq将内容插入到页面中: $('#person_info').html(result);
若是有多项数据须要展现,则须要用到{{#each this}} {{/each}}。 语法相似标签,因此要注意别漏了闭合标签{{/each}}。 {{#each this}} ... {{/each}}中间的内容是模板。this 则是指传入函数的数据data。 用在实现上述例子的代码以下:
1 <body> 2 <div id = "person_info"> 3 {{#each this}} 4 <div class = "person"> 5 <div>姓名:{{name}}</div> 6 <div>出生地:{{home}}</div> 7 <div>职业:{{job}}</div> 8 </div> 9 {{/each}} 10 </div> 11 </body> 12 13 <script> 14 var data =[{ 15 name:'约翰莫里森', 16 home:'美国', 17 job:'摔跤手' 18 }, 19 { 20 name:'Faker', 21 home:'韩国', 22 job:'英雄联盟职业选手' 23 }]; 24 25 var tmpl = $('#person_info').html(); 26 var func = Handlebars.compile(tmpl); 27 var result = func(data); 28 $('#person_info').html(result); 29 </script>
若是对象间所包含的数据域有个别不一样,则须要分支语句进行判断 即{{#if xxx}} {/if}}。 用例以下:
1 <body> 2 <div id = "person_info"> 3 {{#each this}} 4 <div class = "person"> 5 <div>姓名:{{name}}</div> 6 <div>出生地:{{home}}</div> 7 <div>职业:{{job}}</div> 8 {{#if life}} 9 <div>生涯经历:</div> 10 <ul> 11 {{#each life}} 12 <li>{{this}}</li> 13 {{/each}} 14 </ul> 15 {{/if}} 16 </div> 17 {{/each}} 18 </div> 19 </body> 20 21 <script> 22 var data =[{ 23 name:'约翰莫里森', 24 home:'美国', 25 job:'摔跤手' 26 }, 27 { 28 name:'Faker', 29 home:'韩国', 30 job:'英雄联盟职业选手', 31 life:['S3世界总冠军','S4世界总冠军','S5世界总冠军'] 32 }]; 33 34 var tmpl = $('#person_info').html(); 35 console.log(tmpl); 36 var func = Handlebars.compile(tmpl); 37 var result = func(data); 38 $('#person_info').html(result); 39 </script>
最后再附上一篇关于Handlebars语法比较详细的介绍:http://www.ghostchina.com/introducing-the-handlebars-js-templating-engine/
handlebars.js是javascript的一个语义模板库,它经过将data 与 view分离 来快速构建web模板,加载时预编译,必定程度上提升了代码的复用性和可维护性,提升敲代码的效率,总之,是一个轻量级的js 库,功能与如今流行的react 等相比,功能较为单一,可是,对于小型的数据而言,是一个不错的选择。
① 须要引入handlebars.js库
② 经过script标签建立一个模板template,模板内容自定义
③ 定义一个位置(eg:div),显示模板中的内容
④ 经过script标签, 获取模板对象的内容-->compile预编译模板-->定义json数据--> 匹配json数据-->输出模板
示例代码:
- <!DOCTYPE html>
- <html>
- <head>
- <meta charset="UTF-8">
- <title>handlebarsTest</title>
- <script type="text/javascript" src="js/handlebars-v4.0.5.js"></script>
- </head>
- <body>
- <div id="list"></div> // 模板输出位置
- <!--定义template-->
- <script id="entry-template" type="text/x-handlebars-template">
- <div class="myDiv">
- <h2 class="title"> {{title}} </h2>
- {{#persons}}
- <ul class="myUl">
- <li> 姓名:{{this.name}} ,年龄:{{this.age}} , 生日:{{this.birth}}</li>
- </ul>
- {{/persons}}
- <ul class="myUl">
- </ul>
- </div>
- </script>
- <!--获取模板,模板预编译,定义json数据,json数据匹配后,输出模板-->
- <script type="text/javascript">
- var source = document.getElementById("entry-template").innerHTML;
- var template = Handlebars.compile(source);
- var data = {
- title: "person's information",
- persons:[
- {name:"mike", age:12, birth:"11-12"},
- {name:"mary", age:10, birth:"10-28"},
- {name:"maply", age:16, birth:"2-22"}
- ]
- };
- document.getElementById("list").innerHTML = template(data);
- </script>
- </body>
- lt;/html>
handlebars 有一些基本的语法,如变量调用:{{data}},变量不用转义: {{{data}}},block 局部做用域:{{#data}}开始,{{/data}}
结束, 内置的helper:迭代:each ,条件渲染:if else ,unless ;嵌套式针对某一个数据对象:with
内置的helper相对来讲 ,实现某些复杂的功能有些局限性,故能够经过Handlebars.registerHelper()自定义helper,实现较为复杂的
功能,<待续>
这篇文章很不错:http://www.tuicool.com/articles/fqQFN3
一. 为何要使用模板引擎
关于为何要使用模板引擎, 就我如今的项目而言,我还停留在进行发送Ajax请求到后台后,利用模板引擎拼接接受到的JSON字符串,展示到页面的地步. 按照我老师的一句话表达:不用重复制造轮子. 对于为何要使用模板引擎的解释,我看过最好的回答来自知乎上niko的回答:
模板最本质的做用是【变静为动】一切利用这方面的都是优点,不利于的都是劣势。要很好地实现【变静为动】的目的,有这么几点:
1. 可维护性(后期改起来方便);
2. 可扩展性(想要增长功能,增长需求方便);
3.开发效率提升(程序逻辑组织更好,调试方便);
4.看起来舒服(不容易写错);
从以上四点,你仔细想一想,前端模板是否是不管从哪方便优点体现都不是一点两点。其实最重要的一点就是:【视图(包括展现渲染逻辑)与程序逻辑的分离】分离的好处太多了,更好改了,更好加东西了,调试也方便了,看起来也舒服了,应用优秀的开发模式更方便了(mvvc,mvc等).
二. 选择Handlebars的缘由
1. 全球最受欢迎的模板引擎
Handlebars是全球使用率最高的模板引擎,因此当之无愧是全球最受欢迎的模板引擎.Handlebars在许多前端框架中都被引入,好比在MUI和AmazeUI等框架,都推荐使用Handlebars.以AmazeUI为例,AmazeUI的文档中专门为Web组件提供了其Handlebars的编译模板
Amaze UI 提供的开发模板中,包含一个 widget.html 文件,里面展现了 Widget 在纯浏览器环境中的使用。
要点以下:
1.引入 Handlebars 模板 handlebars.min.js; 2.引入 Amaze UI Widget helper amui.widget.helper.js; 3.根据需求编写模板 <script type="text/x-handlebars-template" id="amz-tpl">{{>slider slider}}</script>; 4.传入数据,编译模板并插入页面中。 $(function() { var $tpl = $('#amz-tpl'); var source = $tpl.text(); var template = Handlebars.compile(source); var data = {}; var html = template(data); $tpl.before(html); });
2. 语法简单
Handlebars的基本语法极其简单,使用{{value}}将数据包装起来便可,Handlebars会自动匹配响应的数值和对象.如下是一个最简单的模板:
<div class="demo"> <h1>{{name}}</h1> <p>{{content}}</p> </div>
三.如何使用Handlebars
1. 下载Handlebars
- 经过Handlebars官网下载: http://handlebarsjs.com./installation.html
- 经过npm下载:
npm install --save handlebars
- 经过bower下载:
bower install --save handlebars
- 经过Github下载: https://github.com/daaain/Handlebars.git
- 经过CDN引入:https://cdnjs.com/libraries/handlebars.js
2. 引入Handlebars
经过<script>
标签引入便可,和引入jQuery库相似:
<script src="./js/handlebars-1.0.0.beta.6.js"></script>
3. 建立模板
- 步骤一: 经过一个
<script>
将须要的模板包裹起来 - 步骤二: 在
<script>
标签中填入type
和id
type
类型能够是除text/javascript
之外的任何MIME类型,但推荐使用type="text/template"
,更加语义化id
是在后面进行编译的时候所使用,让其编译的代码找到该模板.
- 步骤三: 在
<script>
标签中插入咱们须要的html代码,根据后台给咱们的接口文档,修改其须要动态获取的内容
<script type="text/template" id="myTemplate">
4. 在JS代码中编译模板
//用jQuery获取模板 var tpl = $("#myTemplate").html(); //预编译模板 var template = Handlebars.compile(tpl); //匹配json内容 var html = template(data); //输入模板 $('#box').html(html);
以上述代码为例进行解释:
- 步骤一: 获取模板的内容放入到tpl中,这里
$("#myTemplate")
中填入的内容为你在上一步建立模板中所用的id
.- 提醒: 这里我使用的
jQuery
的选择器获取,固然,你可使用原生javascript
的DOM
选择器获取,例如:docuemnt.getElementById('myTemplate')
和document.querySelector('#myTemplate')
- 提醒: 这里我使用的
- 步骤二: 使用
Handlebars.compile()
方法进行预编译,该方法传入的参数即为获取到的模板 - 步骤三: 使用
template()
方法进行编译后获得拼接好的字符串,该方法传入的参数即为上一步预编译的模板. - 步骤四: 将编译好的字符串插入到你所但愿插入到的
html
文档中的位置,这里使用的是jQuery
给咱们提供的html()
方法.一样,你也可使用原生的innerHTML
四.案例演示
如下面的慢慢买网站为例,该项目中的手机列表,是经过Ajax动态获取的,咱们不可能在html文档中写入所有的手机列表代码,这是不可能的.因此咱们须要经过Handlebars来帮咱们将后台传递过来的数据动态的显示到html文档中.

1. 在HTML中引入:Handlebars,jQuery和本页的Js代码
<script src="./lib/bootstrap/js/jquery-3.2.1.js"></script> //Handlebars <script src="./js/handlebars-1.0.0.beta.6.js"></script> //jQuery <script src="./js/product.js"></script> //本页的Js代码
2. 建立模板
在未插入模板的状况下,页面显示以下,如今咱们来使用Handlebars让数据动态的显示在网页上.

<!--定义模板 --> <script type="text/template" id="product-list-tepl">
以上模板中的{{#}}为Handlebars的helper语法,能够实现Javascript中的逻辑和循环运算.更多使用方法能够参考: http://www.ghostchina.com/introducing-the-handlebars-js-templating-engine/
3. 在JS代码中编译模板
//定义getList()函数来发送Ajax请求,传递的参数为后台给的接口文档中定义的参数 function getList(categoryId,pageid){ //调用jQuery的Ajax()方法来发送Ajax请求 $.ajax({ type:'get', url:'http://182.254.146.100:3000/api/getproductlist', data:{ pageid:pageid||1, categoryid:categoryId }, success:function(data){ //用zepto获取模板 var tpl = $("#product-list-tepl").html(); //预编译模板 var template = Handlebars.compile(tpl); //匹配json内容 var html = template(data); //插入模板,到ul中 $('.product-list ul').html(html); } }) } //入口函数 $(function(){ //获取到查询字符串的id var categoryId = Number(GetQueryString("categoryid")); //getQueryString()是获取上一步传递过来的查询字符串的方法 //调用定义的getList()获取手机列表 getList(categoryId); }) //获取上一步传递过来的查询字符串的方法 function GetQueryString(name) { var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)"); var r = window.location.search.substr(1).match(reg); if (r != null) return unescape(r[2]); return null; }
4. 插入模板后的页面以下

经过上面的案例,我相信你们应该可以明白模板引擎的强大,咱们只须要在页面中写好一个手机列表的HTML代码,便可动态获取后台传递过来的全部信息,从而在页面中进行展现.
注意: 在实际开发中,咱们经过Ajax发送请求时所须要传递的参数,和获取到的JSON或其余格式的数据.皆是须要经过后台给定的接口文档为准.
做者:Lee_tanghui
连接:https://www.jianshu.com/p/2ad73da601fc
來源:简书
著做权归做者全部。商业转载请联系做者得到受权,非商业转载请注明出处。
前端数据模板handlebars与jquery整合
1、简介
handlebars是前端数据模板,在此介绍handlebars主要指令,以及与jquery整合的开发流程。
2、handlebars主要指令
handlebars中的指令与数据变量,都以两个大括号(即{{和}})包围,指令以#开始,以/结尾。
一、循环指令#each
可遍历对象和数组,遍历中或获取的数据有:
a)@index ,遍历过程当前元素的索引;
b)@key ,遍历过程当中,当前元素的名称(或数组元素的索引)
c)this ,遍历过程当中,当前元素的值;
二、条件判断指令#if和#unless
a)#if /else,当if内的数据有且不为null时,返回true;
b)#unless,与指令相反
三、上下文指令#with
为模板的一块区域定义上下文
3、handlebars与jquery整合的开发流程
一、编写handlebars的jquery插件handlebars-jquery.js ,内容以下:
(function($){ var compiled = {}; $.fn.template = function(data){ var template = $.trim($(this).first().html()); if(compiled[template] == undefined){ compiled[template]=Handlebars.compile(template); } return $(compiled[template](data)); }; })(jQuery)二、在页面引入jquery包、handlebars包、handlebars的jquery插件包,以下:
<script src="../lib/js/jquery-3.0.0.js"></script> <script src="../lib/js/handlebars-v4.0.10.js"></script> <script src="../lib/js/handlebars-jquery.js"></script>三、编写数据(实际使用中,可经过ajax从后台获取数据),内容以下:
var data={ stus:[ {id:100,name:"apple1",age:11}, {id:200,name:"apple2"}, {id:300,name:"apple3",age:33}, ], stu:{id:300,name:"apple3",age:33} };四、编写数据模板(注意:script标签的type类型为text/x-handlebars-template,内容以下:
<script id="stuTempId" type="text/x-handlebars-template"> <section> <ul> {{#each stu}} <li>{{@index}}:{{@key}}:{{this}}</li> {{/each}} </ul> </section> <table border="1px"> <tr><td>序号</td><td>学号</td><td>姓名</td><td>年龄</td><td>序号</td><td>姓名</td><td>年龄</td></tr> {{#each stus}} <tr> <td>{{@index}}</td> <td>{{id}}</td> <td>{{name}}</td> <td>{{#if age}}{{age}}{{else}}未填写{{/if}}</td> <td>{{@key}}</td> <td>{{this.name}}</td> <td>{{#with this}}{{age}}{{/with}}</td> </tr> {{/each}} </table> </script>五、将数据绑定到模板上,并在页面展现,如:
$("#stuInfoId").empty().append($("#stuTempId").template(data).filter("*")); //filter("*")用于匹配全部html节点,过滤掉文本节点以上完整的代码以下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>templateStudy</title> <script src="../lib/js/jquery-3.0.0.js"></script> <script src="../lib/js/handlebars-v4.0.10.js"></script> <script src="../lib/js/handlebars-jquery.js"></script> <script id="stuTempId" type="text/x-handlebars-template"> <section> <ul> {{#each stu}} <li>{{@index}}:{{@key}}:{{this}}</li> {{/each}} </ul> </section> <table border="1px"> <tr><td>序号</td><td>学号</td><td>姓名</td><td>年龄</td><td>序号</td><td>姓名</td><td>年龄</td></tr> {{#each stus}} <tr> <td>{{@index}}</td> <td>{{id}}</td> <td>{{name}}</td> <td>{{#if age}}{{age}}{{else}}未填写{{/if}}</td> <td>{{@key}}</td> <td>{{this.name}}</td> <td>{{#with this}}{{age}}{{/with}}</td> </tr> {{/each}} </table> </script> <script type="text/javascript"> $(function () { var data={ stus:[ {id:100,name:"apple1",age:11}, {id:200,name:"apple2"}, {id:300,name:"apple3",age:33}, ], stu:{id:300,name:"apple3",age:33} }; $("#stuInfoId").empty().append($("#stuTempId").template(data).filter("*")); //filter("*")用于匹配全部html节点,过滤掉文本节点 }); </script> </head> <body> <section id="stuInfoId"></section> </body> </html>输出:
- 0:id:300
- 1:name:apple3
- 2:age:33
序号 | 学号 | 姓名 | 年龄 | 序号 | 姓名 | 年龄 |
0 | 100 | apple1 | 11 | 0 | apple1 | 11 |
1 | 200 | apple2 | 未填写 | 1 | apple2 | |
2 | 300 | apple3 | 33 | 2 | apple3 | 33 |
https://cnodejs.org/topic/56a2e8b1cd415452622eed2d
提示:文中 “{ {” 、“} }”,在实际使用中用双花括号,中间无空格。
写这篇文章也是由于当初我为了实现一个模板分页的某个功能(这个问题以及解决方法最后说),花了很多时间将官网看了几遍,试验了好久以后依然没有成功,而后凭着一股崛起继续钻研终于解决了这个问题。此时发现handlebars的东西基本上也了解得差很少了,因此干脆抽时间写篇文章整理一下。
为何须要模板引擎
关于前端的模板引擎,我用一个公式来解释
模板引擎 模板 + 数据 ========> html页面
模板引擎就像是html的解析生成器,将对应的模板填充完数据以后生成静态的html页面。它能够在浏览器端(好比angular中指令所用的模板)也能够在服务器端执行,不过通常用于服务器端。由于它的一个做用是抽象公共页面来重用,若是在服务端填充数据,能够减小回填数据给页面的ajax请求,从而提高浏览器端总体页面渲染速度。
那些年我用过的模板引擎
接触过的模板引擎不算多,最先应该是jsp,本质上也是一种模板引擎,再到功能稍微强大的freemarker,这两种都是属于java语系的。js语系的jade和ejs我都有所接触,不过不经常使用,jade那种类python的语法规则以及较低的解析效率都让我不敢兴趣,Express框架也只是早起将其做为模板引擎。后来换成了强大的ejs,不管是功能仍是写法上都接近jsp了。直到最新的Express4发布,默认改成了弱逻辑的比较简洁的模板引擎handlebars。
我使用handlebars有如下几个缘由:
- 此次新项目前端框架搭建基于Express4,模板引擎只能在ejs/jade/hogan/hbs中选择一个。
- 默认是handlebars,虽不知道缘由,想必有其缘由。
- 看过“去哪儿”的前端技术分享,他们就是在handlebars上进行封装的,证实已经有人填过坑了,能够一试。
- 开始比较看好ejs,可是官网文档被强了,相比之下handlebars的文档比较清晰,还有实例,虽然逻辑结构比较混乱,可是基本无障碍。
码解handlebars
运行环境:Express四、hbs4 未接触Express或hbs的能够先看这里
初级玩家:表达式
数据:
{ title: 'Express', obj:{ version: 'v4.3', category: 'node', "date~": '2016' } }
模板:
<p>{ {title} }</p> <p>{ {obj.version} }</p> <p>{ {obj/category} }</p> <p>{ {obj.date~} }</p>
html页面:
Express v4.3 node
handlebars中变量都添加双花括号来表示(相似Angular),对比ejs的"<%%>“来讲看起来没什么区别,其实这是很人性化的,想一下你键盘上的位置,再考虑按这几个字符的难易程度你就懂了。 其中要访问变量的属性值时能够用相似json格式的”.",也能够用"/"。
其中变量名不可包含如下字符。若是包含则不被解析,如上的"{{obj.date~}}"。
空格 ! " # % & ' ( ) * + , . / ; < = > @ [ \ ] ^ ` { | } ~
但能够用 " , ’ , [] 来转译这些特殊字符。
这一条规则意味着 “&&”,"||","!"这类逻辑判断是不能出如今表达式中的! (看着这一条是否是以为弱爆了,要否则怎么叫若逻辑模板引擎呢~哈哈,不过固然有另外的解决办法)。
中级玩家:helper
英语水平有限,实在找不到一个恰当的词来翻译它了。能够理解为它是注入到模板中的一个函数,用来接收参数并进行逻辑处理。
默认helper
if else
{ {#if author} } <h1>{ {firstName} } { {lastName} }</h1> { {else} } <h1>Unknown Author</h1> { {/if} }
{ {#if isActive} } <img src="star.gif" alt="Active"> { {else if isInactive} } <img src="cry.gif" alt="Inactive"> { {/if} }
和通常的编程语言的 if-else 代码块是差很少的,不过再次重申因为上面提到的特殊字符,因此if条件中是不能有逻辑表达式的,只能是变量或者值。
unless
仍是由于上面提到的那些字符,handlebars不支持逻辑非("!"),因此又有了一个与if相反的helper
{ {#unless license} } <h3 class="warning">WARNING: This entry does not have a license!</h3> { {/unless} }
上面这段代码就等价于
{ {#if license} } { {else} } <h3 class="warning">WARNING: This entry does not have a license!</h3> { {/if} }
each
都知道each至关于for循环。不过有些地方须要注意:
- 能够用相对路径的方式来获取上一层的上下文。(上下文概念跟js中的上下文差很少,好比在each passage代码块内,每一次循环上下文一次是passage[0],passage[1]…)
- 一些默认变量,@first/@last 当该对象为数组中第一个/最后一个时返回真值。若是数组成员为值而非对象,@index表示当前索引值,能够用@key或者this获取当前值
- 能够用
as |xxx|
的形式给变量起别名,循环中经过别名能够引用父级变量值。固然也能够经过相对路径的方式引用父级变量。
{ {#each passage} } { {#each paragraphs} } { {@../index} }:{ {@index} }:{ {this} }</p> { {else} } <p class="empty">No content</p> { {/each} } { {/each} }
{ {#each array as |value, key|} } { {#each child as |childValue, childKey|} } { {key} } - { {childKey} }. { {childValue} } { {/each} } { {/each} }
同时也能够用来遍历对象,这时@key表示属性名,this表示对应的值
{ {#each object} } { {@key} }: { {this} } { {/each} }
with
相似js中的with,能够配合分页使用,限定做用域。
{ {#with author as |myAuthor|} } <h2>By { {myAuthor.firstName} } { {myAuthor.lastName} }</h2> { {else} } <p class="empty">No content</p> { {/with} }
lookup
这个用于如下这种并列数组的状况,能够按照索引来找兄弟变量对应的值。理解起来有些困难,直接看代码
{ groups: [ {id: 1, title: "group1"}, {id: 2, title: "group2"}, ], users: [ {id:1, login: "user1", groupId: 1}, {id:2, login: "user2", groupId: 2}, {id:3, login: "user3", groupId: 1} ], infos: [ 'a','b','c' ] }
<table> { {#each users} } <tr data-id="{ {id} }"> <td>{ {login} }</td> <td data-id="{ {groupId} }">{ {lookup ../infos @index} }</td> </tr> { {/each} } </table>
user1 a user2 b user3 c
这里在users数组中按照索引值引用infos数组中对应的值,若是想引用groups中的groupId呢?很简单,用with。
<table> { {#each users} } <tr data-id="{ {id} }"> <td>{ {login} }</td> <td data-id="{ {groupId} }">{ {#with (lookup ../groups @index)} }{ {title} }{ {/with} }</td> </tr> { {/each} } </table>
自定义helper
内置的helper不够强大,因此一般须要写js代码自定义helper,先看一个简单的单行helper。
行级helper
传值
数值、字符串、布尔值这种常规数据能够直接传入,同时也能够传递JSON对象(但只能传一个),以key=value这种形式写在后面,最后就能够经过参数的hash属性来访问了。
模板
{ {agree_button "My Text" class="my-class" visible=true counter=4} }
代码
hbs.registerHelper('agree_button', function() { console.log(arguments[0]);//==>"My Text" console.log(arguments[1].hash);//==>{class:"my-class",visible:true,conter:4} }
传变量
传变量时能够用this指针来指代它访问属性,经过逻辑判断后能够返回一段html代码,不过太建议这样作。考虑之后的维护性,这种html代码和js代码混合起来的维护性是比较差的,若是要抽象层组件仍是使用分页比较好。
模板:
{ {agree_button person} }
注册helper:
hbs.registerHelper('agree_button', function(p) { console.log(p===this);//==> true var blog = hbs.handlebars.escapeExpression(this.person.blog), name = hbs.handlebars.escapeExpression(this.person.name); return new hbs.handlebars.SafeString( "<a href='"+blog+"'>"+ name + "</button>" ); });
数据:
var context = { person:{name: "亚里士朱德", blog: "https://yalishizhude.github.io"} }; };
html页面:
<a href="https://yalishizhude.github.io">亚里士朱德</a>
当内容只想作字符串解析的时候能够用 escapeExpression 和 SafetString 函数。
块级helper
块级helper获取参数的方式跟以前差很少,只是最后多了一个参数,这个参数有两个函数fn
和revers
能够和else
搭配使用。后面将会讲解。
模板:
{ {#list nav} } <a href="{ {url} }">{ {title} }</a> { {/list} }
注册helper:
Handlebars.registerHelper('list', function(context, options) { var ret = "<ul>"; for(var i=0, j=context.length; i<j; i++) { ret = ret + "<li>" + options.fn(context[i]) + "</li>"; } return ret + "</ul>"; });
数据:
{ nav: [ { url: "https://yalishihzude.github.io", title: "blog" }, { url: "https://www.github.com/yalishizhude", title: "github" }, ] }
html页面:
<ul> <li> <a href="https://yalishizhude.github.io">blog</a> </li> <li> <a href="https://www.github.com/yalishizhude">github</a> </li> </ul>
自定义helper
each的index变量比较经常使用,可是它是从0开始的,每每不符合业务中的需求,这里写个helper来扩展一下。
注册helper:
hbs.registerHelper('eval', function(str, options){ var reg = /\{\{.*?\}\}/g; var result = false; var variables = str.match(reg); var context = this; //若是是each if(options.data){ context.first = context.first||options.data.first; context.last = context.last||options.data.last; context.index = context.index||options.data.index; context.key = context.key||options.data.key; } _.each(variables, function(v){ var key = v.replace(/{ {|} }/g,""); var value = typeof context[key]==="string"?('"'+context[key]+'"'):context[key]; str = str.replace(v, value); }); try{ result = eval(str); return new hbs.handlebars.SafeString(result); }catch(e){ return new hbs.handlebars.SafeString(''); console.log(str,'--Handlerbars Helper "eval" deal with wrong expression!'); } });
模板:
{ {#each list} } { {eval '{ {index} }+1'} } { {/each} }
上面说到if不支持复杂的表达式,若是是“&&”操做还能够用子表达式来实现,更加复杂的就很差办了,这里我写了一个helper来实现。
注册helper:
hbs.registerHelper('ex', function(str, options) { var reg = /\{\{.*?\}\}/g; var result = false; var variables = str.match(reg); var context = this; _.each(variables, function(v){ var key = v.replace(/{ {|} }/g,""); var value = typeof context[key]==="string"?('"'+context[key]+'"'):context[key]; str = str.replace(v, value); }); try{ result = eval(str); if (result) { return options.fn(this); } else { return options.inverse(this); } }catch(e){ console.log(str,'--Handlerbars Helper "ex" deal with wrong expression!'); return options.inverse(this); } });
模板:
{ {#ex "{ {state} }==='submiting'"} } <i class="icon cross-danger">1</i> { {else} } <i class="icon cross-success">2</i> { {/ex} }
先将整个逻辑表达式做为一个字符串传入,而后替换其中的变量值,最后用eval函数来解析表达式,同时增长异常处理。
高级玩家:partial
比较推崇使用分页来实现组件化。分页跟helper同样须要先注册。在hbs模块中能够批量注册,比较简单。
hbs.registerPartials(__dirname + '/views/partials');
基础引用
用“>”来引用模板,这种状况通常用来处理页头页尾这种简单的分页。后面能够传入参数。 { {> myPartial param} }
当使用块级表达式时,咱们一般添加“#”,而分页是“>”,因此块级分页使用“#>”,这里表示若是layout分页不存在则显示块内的内容My Content。
{ {#> layout } } My Content { {/layout} }
动态分页
固然也能够用表达式来代替分页名称
{ {> (whichPartial) } }
当分页中一部分代码是固定的,另外一部分是变化的时候,能够在分页中添加“@partial-block”,这时当引用这个分页时,在内部编写代码将会填充到这个位置。
partial.hbs:
亚里士朱德 { {> [@partial-block](/user/partial-block) } }
模板:
{ {#>partial} } https:yalishizhude.github.io { {/partial} }
html页面:
亚里士朱德 https:yalishizhude.github.io
内联分页
当有多段代码须要填充到分页时,能够用以下方法。分页中内嵌分页变量,模板中经过内联分页的方式传入。
模板:
{ {#> partial} } { {#*inline "nav"} } 亚里士朱德 { {/inline} } { {#*inline "content"} } https://yalishizhude.github.io { {/inline} } { {/partial} }
partial.hbs:
<div class="nav"> { {> nav} } </div> <div class="content"> { {> content} } </div>
html页面:
<div class="nav"> 亚里士朱德 </div> <div class="content"> https://yalishizhude.github.io </div>
大师级玩家:API
本文列举的只是handlebars中最重要和经常使用的功能,更多细碎的功能能够去查看 官方API。
开头的问题
我想将导航条写成一个分页(partial),导航条左边的文字标题是能够经过参数传递的,可是右边的内容多是文字、图片其它元素,须要具体业务自定义实现。我又不想把html代码写在js中,因此但愿在模板中将这段未知的模板代码填充到分页中进行展示。我在官网文档中找到了 {{>@partial-block}}来实现此功能,可是本机实验一直解析报错。 解决过程: 这个问题缘由可能有两个,一是官方文档有错,二是本机环境的插件有问题(Express用hbs模块,该模块封装了handlebars引擎模块)。为了验证官方文档的正确性,我找到了一个在线handlebars解析器,输入文档中的代码时能够正确解析,那么只可能出如今hbs模块了。这时在github上找到hbs模块最新版本为4,查看本地版本为3,更新后果真能够正常解析了。
总结
handlebars让咱们看到一个好的插件应该有的特征:
- 可识别性。接口简单,使用方便,容易上手。
- 高可用性。自带经常使用一些功能(helper),不求多而求精。
- 可扩展性。复杂的业务逻辑,开发人员能够自定义helper去扩展和实现。
做者:亚里士朱德 博客:http://yalishizhude.github.io