由於作者翻譯會加入 自己的理解 以便自己學習和使用, 如果英文好的同學可看下面 如文章中有翻譯錯誤還請留言. 交流並改正. (:
文章來自net|tuts+ 原文地址 請點擊
======================Enein翻譯=========================
如果你發現你的站內信息老是定期的改變, 那么你可以嘗試使用 Handlebars , Handlebars 是一個 "template processor" 模板引擎, 它會動態的生成你HTML頁面. 省祛你手動更新的時間, 在這篇文章里我將介紹Handlebars, 並教你怎么為你的站點create一個簡單的template.
Site template
為什么要讓你的頁面create一個模板, 這里有兩個主要的原因:
【1】 創建模板可以使你的邏輯代碼與view分離, 來接近實現 view/controller 模式.
【2】 模板使用你的代碼變得 清晰、可維護性提高.
這使得你很快就可以更新你的網站. 你不必為Handlebars設置一個站點, 只需要使用一些語法結構 這樣你的網站 頁面上會沒有固定的數據, 下面介紹一些基礎.
The Basics
Handlebars使用template運行Json格式來生成你的頁面, 這些template基本上都是HTML和一些占位和一些占位符(它允許你注入數據) 例:
這段代碼是告訴用戶你已經登陸:
<h1>Welcome back, {{name}}</h1>
這個
{{name}}
屬性是用戶名稱是動態注入到頁面里來的. 這個占位符是使用一種類似Json結構, 這可能是最基本的例子, 但是你將看到的每一個都會基於這個簡單的理念. 讓我們繼續看這個 "數組".
Arrays
Handlebars中有一些內置的方法可以提供你處理復雜的數據, “
each”只是其中之一, 這個標簽是實現一個 iterator 來實現動態create頁面. For example:
你能看到, 這段代碼比常規的代碼要清晰很多, 類似使用 PHP的loop/Javascript的動態append頁面. Handlebars的語法不具有 "intrusive"干擾性, 所以它是很容易理解的. 你可能不會注意到 {{this}} 意思是取出loop中當前的 element。<
table
>
<
tr
>
<
th
>Local Concerts</
th
>
</
tr
>
{{#each Concerts}}<
tr
>
<
td
>{{this}}</
td
>
</
tr
>
{{/each}}</
table
>
這個例子, 實現數組里有簡單的值還是很好的, 但是如何處理更復雜的數據? 好吧, 你基本上是做同樣的事情, 例如, 我們按照下面的數據,將寫一個template
[ { Name : "Band", Date : "Aug 14th, 2012", Albums : [ { Name : "Generic Name" }, { Name : "Something Else!!" } ] }, { Name : "Other Guys", Date : "Aug 22nd, 2012" Albums : [ { Name : "Album One" } ] } ]我們很容易使用下面的template來實現上面的數據顯示:
<
table
>
<
tr
>
<
th
>Band Name</
th
>
<
th
>Date</
th
>
<
th
>Album Name</
th
>
</
tr
>
{{#each Bands}}
<
tr
>
<
td
>{{Name}}</
td
>
<
td
>{{Date}}</
td
>
<
td
>{{Albums.0.Name}}</
td
>
</
tr
>
{{/each}}
</
table
>
在Handlebars中, 你甚至能訪問內嵌屬性, like in (Albums.0.Name). 當然你也可再使用一個each來loop你的數組. 你應該注意到了, 上面的例子中使用的 . 來訪問屬性, 你也能使用 ../ 來訪問 父級的屬性.
如果數組為空沒有任何數據, 你當然不希望有一個空的 table, Handlebars 很友好的提供了
if else 和
unless 語法, 這個
if ... else 在一些程序語言中都工作的很好, 如果這個Object是
false或是假類型, 然后 else 將執行. 相反,
if 代碼塊將會執行. 這個
unless是非常有意思的; 它是一個相反的
if代碼塊, 如查表達式為 true, 這個unless塊不執行. 所以讓我們來加入這個方法.
{{#if Bands}}
<
table
>
<
tr
>
<
th
>Band Name</
th
>
<
th
>Date</
th
>
<
th
>Album Name</
th
>
</
tr
>
{{#each Bands}}<
tr
>
<
td
>{{Name}}</
td
>
<
td
>{{Date}}</
td
>
<
td
>{{Albums.0.Name}}</
td
>
</
tr
>
{{/each}}</
table
>
{{else}}<
h3
>There are no concerts coming up.</
h3
>
{{/if}}
Custom Helpers
Handlebars 可以提供給你自定義這個json語法的能力. 在Handlebars里只是簡單的注冊一下你的Function, 然后一些template在編譯的時候就可以使用你的語法. 下面的有兩種類型可以讓你去創建:
- Function helpers 是基本的Function函數, 注冊一次, 就可以在Handlebars template中的任何地方使用. Handlebars會寫這個Function的回值到template上.
- Block helpers 是類似 if , each, etc 它們允許人改變 上面文里的
- 讓我展示給你一個簡單的例子. 首先, 我將會注冊一個function helper 通過以下代碼:
-
Handlebars.registerHelper("Max", function(A, B){ return (A > B) ? A : B; });
這個 registerHelper的第一個參數是這個 helper 的名子, 我將會在template里使用它. 這個第二個參數, 是一個匿名函數. - 如何使用請看下面的例子:
-
{{Max 12 45}}
這個template 使用 Max 語法 , 和解析 12 和 45 到這個函數里, Handlebars 函數 helper里支持多個參數, 你可以直接插入數據 到template本身, 或者你能使用Json結構的屬性.
現在讓我們看一下Block helper, Block helper, 允許你在代碼運行前設置這個context, 例如下面這個Object:
{ Name: "Parent", Sub: { Name: "Child" } }這里有兩個key相同的Name屬性, 與一個Helper來實現 即可以調用父級的Name, 又可以調用 child的Name:
Handlebars.registerHelper("BothNames", function(context, options){ return options.fn(context) + options.fn(context.Sub); });template看起來像這樣:
{{#BothNames this}}
<
h2
>{{Name}}</
h2
>
{{/BothName}}
這個結束的語法名稱 是告訴Handlebars這是一個block helper,和關閉它。
這個 options.fn 函數運行模板內部的塊. 你可以把context傳給它.
現在基礎已經告一段落, 讓我們來一個完整的Demo吧.
Building a Site Template
我們將使用template來構建一個食譜網站. 這將將會讓你這將會讓你很好的明白, Handlebars通過API取得數據和在模板中如何解析.
Setting up a Handlebars project
我們必須首先加載我們的 template script, 但為了做到這一點, 我們需要創建一個HTML文件和包含我們的Handlebars.js文件
<
html
>
<
head
>
<
title
>Handlebars Demo</
title
>
<
script
type
=
"text/javascript"
src
=
"Handlebars.js"
></
script
>
</
head
>
<
body
>
<
script
id
=
"Handlebars-Template"
type
=
"text/x-handlebars-template"
>
</
script
>
</
body
>
</
html
>
為了簡單, 你能存儲你的template到<script>里和加載它通過JavaScript.這比它直接存儲到JavaScript里一個變量干凈多了.
現在讓我們討論這個app如何工作. 首先使這個app連接一個API去取到一些食譜信息. 下一步, 我們通過把這些信息放到Handlebars和通過template運行它. 最后, 我們通過新生成的頁面把body里的內容替換掉, 它是一個相當直接的一個過程; 所以, 讓我們開始在</body>之前加入第二個script代碼塊來初始化Ajax變量.
<
script
>
var Ajax = (window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP");
Ajax.onreadystatechange = function () {
if (Ajax.readyState == 4 && Ajax.status == 200)
{
//Parse the JSON data
var RecipeData = JSON.parse(Ajax.responseText);
//Get the Template from above
var Source = document.getElementById("Handlebars-Template").textContent;
//Compile the actual Template file
var Template = Handlebars.compile(Source);
//Generate some HTML code from the compiled Template
var HTML = Template({ Recipes : RecipeData });
//Replace the body section with the new code.
document.body.innerHTML = HTML;
}
}
Ajax.open("GET","Recipe.php", true);
Ajax.send();</
script
>
這是通過一個template去動態的編譯生成html。 理論上你可以直接把Json數據放到Handlebars中, 但是你運行時會出現跨域問題。 代替的方法是使用PHP里的echo把它變為一個JavaScript的變量.所以在運行這個模板之前我會將所以的操作分到"Recipe.php"里去做. 讓我們看一下這個PHP文件。
Geting The Date
這個Yummly API 是相當簡單, 沒有身份驗證系統; 你只需要注冊, 取得驗證信息加入到URL中,如果你想你可以通過 echo 來處理這些數據.
但我想要更詳細的信息為每一個食譜, 在這之前, 我調用兩個請求.
matches; //Cycle Through The Recipes and Get full recipe for each foreach($Recipes as $Recipe) { $ID = $Recipe->id; $R = json_decode(file_get_contents("http://api.yummly.com/v1/api/recipe/" . $ID . "?_app_id=" . $UserID . "&_app_key=" . $UserKey . "&images=large")); //This is the data we are going to pass to our Template array_push($Json, array( Name => $R->name, Ingredients => $R->ingredientLines, Image => $R->images[0]->hostedLargeUrl, Yield => $R->yield, Flavors => $R->flavors, Source => array( Name => $R->source->sourceDisplayName, Url => $R->source->sourceRecipeUrl ) )); } //Print out the final JSON object echo json_encode($Json); ?>
下面是Handlebars template 程序, 你只需要幾行代碼就可以生成一個完整的網站.
<
script
id
=
"Handlebars-Template"
type
=
"text/x-handlebars-template"
>
<
div
id
=
"Content"
>
<
h1
>ΞRecipeCards
<
span
id
=
'BOS'
>Recipe search powered by
<
a
id
=
'Logo'
href
=
'http://www.yummly.com/recipes'
>
<
img
src
=
'http://static.yummly.com/api-logo.png'
/>
</
a
>
</
span
>
</
h1
>
{{#each Recipes}}
<
div
class
=
'Box'
>
<
img
class
=
'Thumb'
src
=
"{{{Image}}}"
alt
=
"{{Name}}"
>
<
h3
>{{Name}} <
a
id
=
'Logo'
href
=
"{{Source.Url}}"
> - {{Source.Name}}</
a
></
h3
>
<
h5
>{{getFlavor Flavors}}</
h5
>
<
h5
>{{Yield}}</
h5
>
<
p
>Ingredients:</
p
>
<
ul
>
{{#each Ingredients}}
<
li
>{{this}}</
li
>
{{/each}}
</
ul
>
</
div
>
{{/each}}
</
div
>
</
script
>
讓我們運行這段代碼. 第6行只是一個Logo然后, 然后列出所有的食譜, 我們創建一個食譜卡片通過一個圖片, 一個名稱和一個配料.
這個Yummly API 返回味道數據. 我寫一個 Function helper, 叫 getFlavor 取出味道數據到一個盤子里.為了使這個模板進行工作, 在解析template之前我們需要加載 getFlavor , 所以在Ajax Code 之前加入.
Handlebars.registerHelper("getFlavor", function(FlavorsArr){ var H = 0; var Name = ''; for(var F in FlavorsArr) { if(FlavorsArr[F] > H) { H = FlavorsArr[F]; Name = F; } } return "This Dish has a " + Name + " Flavor"; });
現在, 當Handlebars解析到 getFlavor 時, 它調用相關功能和檢索出味道信息.
在這點上, 你可以自由的設計你的模板. 但是你可能看到這個過程是很緩慢的. 這主要是由於這個三個請求調用是在 Handlerbars template加載之前的.顯然這不是一個好的辦法. 但precompiling你的template會有所幫助
Precompiling
你有兩個不同的操作, 這第一個是預編譯template, 這是為了降低加載時間, 和你將要包涵一個Handlebars compiler 到你的頁面
我們的問題是 瀏覽器與API通信問題, 如果你想要預處理你的template, 你可以下載Node.js包 通過 npm 下面命令:
npm install handlebars -g你可能需要 root 權限, 安裝完成, 你能為你的template創建一個文件和編譯它像這樣
handlebars demo.handlebars -f demo.js
你可以給你的template文件一個“.handlebars”擴展名. 這不是強制的, 如果你的名稱是類似 demo.html, 然后你的template文件名稱是將會是"demo.html"有用部分僅僅是"demo",命名你的template后, 運行Handlebars后將內容輸出到文件中,使用如下
var template = Handlebars.templates['demo']; var html = template({ Your Json Data Here });
但是, 正如我前面所說的, 這並沒有幫助我們解決問題. 我們該怎么做? 好吧, 我們能precompile和輸出實體文件. 這使我們可以運行最終html的模板數據 --- 換句話說也就是 caching。
不幸的是. 在客戶端Javascript沒有文件IO功能。 所以容易做到的方法就是輸出html后手動保存它. 利用API' caching ! 並確保在保存靜態頁面時先取得數據.
附錄
[1] Yummly: Yummly 是一個語義食譜搜索引擎,擁有50多萬份食譜,用戶可以根據口味、原料、價格、營養等搜索到所需要的食品。Yummly會根據用戶過去的搜索,不斷學習喜好,從而推薦可能會感興趣的食品給你。
[2] Helper: 文章里使用的helper 是指Handlebars里有一個語法。
======================Enein翻譯=========================
由於作者翻譯會加入 自己的理解 以便自己學習和使用. 如有轉載請注明出處謝謝. 如文章中有翻譯錯誤或有更好的方案還請留言. 交流並改正. (: