使用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文檔中.
![慢慢賣的項目](http://static.javashuo.com/static/loading.gif)
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讓數據動態的顯示在網頁上.
![未使用Handlebars的頁面](http://static.javashuo.com/static/loading.gif)
<!--定義模板 --> <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. 插入模板後的頁面以下
![使用Handlebars後的頁面](http://static.javashuo.com/static/loading.gif)
經過上面的案例,我相信你們應該可以明白模板引擎的強大,咱們只須要在頁面中寫好一個手機列表的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