document.write('<h1>Please Don\'t Do This</h1>'); document.write('<p><span class="code">document.write</span> is naughty,\n'); document.write('and should be avoided at all costs.</p>'); document.write('<p>Today\'s date is ' + new Date() + '.</p>');
問題出如今這裏:切換上下文環境是困難的。若是你寫了大量的JavaSctipt,混合在HTML中會引發麻煩和混亂; 由JavaScript生成的HTML充滿了問題:html
模板解決了在目標語言中編寫代碼的問題,同時也讓插入動態數據成爲了可能; 只有在某些最簡單的狀況下才會使用JavaScript生成HTML。node
一些可供參考的準則: 參考推薦文章git
Template-Engine-Choosergithub
doctype html <!DOCTYPE html> html(lang="en") <html lang="en"> head <head> title= pageTitle <title>Jade Demo</title> script. <script> if (foo) { if (foo) { bar(1 + 5) bar(1 + 5) } } body </script> <body> h1 Jade <h1>Jade</h1> #container <div id="container"> if youAreUsingJade p You are amazing <p>You are amazing</p> else p Get on it! p. <p> Jade is a terse and Jade is a terse and simple templating simple templating language with a language with a strong focus on strong focus on performance and performance and powerful features. powerful features. </p> </body> </html>
Jade無疑是少打了不少字,由於再也不有尖括號和結束標記。取而代之,它依賴縮進和一些常識性規則,從而更容易表達出本身想要的。Jade具備一個額外的優點:理論上講,當HTML自身發生改變時,你能夠輕鬆地將Jade定位於HTML版本的最新版本,從而讓你的內容更具「前瞻性」。express
理解模板引擎的關鍵在於context(上下文環境):當渲染一個模板時,便會傳遞給模板引擎一個對象,叫做上下文對象,它能讓替換標識運行。npm
例如,若是上下文對象是{ name: 'Buttercup' },模板是<p>Hello, {{name}}!</p> ,則 {{name}}會被Buttercup替換。若是向模板中傳遞HTML文本會發生什麼呢?例如,上下文換成{ name: '<b>Buttercup</b>' },使用以前的模板獲得的結果將是<p>Hello,<b>Buttercup<b></p>,這或許並非你想要的。要想解決這個問題,用三個大括號代替兩個就能夠了:{{{name}}}。
{{! super-secret comment }} <!-- not-so-secret comment -->
假設這是一個服務器端模板,上面的super-secret comment將不會被傳遞到瀏覽器,然而若是用戶查看HTML源文件,下面的not-so-secret comment就會被看到。數組
塊級表達式瀏覽器
{ currency: { name: 'United States dollars', abbrev: 'USD', }, tours: [ { name: 'Hood River', price: '$99.95' }, { name: 'Oregon Coast', price: '$159.95' }, ], specialsUrl: '/january-specials', currencies: [ 'USD', 'GBP', 'BTC' ], }
傳遞到以下模板:緩存
<ul> {{#each tours}} {{! I'm in a new block...and the context has changed }} <li> {{name}} - {{price}} {{#if ../currencies}} ({{../../currency.abbrev}}) {{/if}} </li> {{/each}} </ul> {{#unless currencies}} <p>All prices in {{currency.name}}.</p> {{/unless}} {{#if specialsUrl}} {{! I'm in a new block...but the context hasn't changed (sortof) }} <p>Check out our <a href="{{specialsUrl}}">specials!</p> {{else}} <p>Please check back often for specials.</p> {{/if}} <p> {{#each currencies}} <a href="#" class="currency">{{.}}</a> {{else}} Unfortunately, we currently only accept {{currency.name}}. {{/each}} </p>
each
輔助方法,這使咱們可以遍歷一個數組; 在下級塊中,若是想訪問currency對象,就得使用../來訪問上一級上下文。if
輔助方法:
在Handlebars中,全部的塊都會改變上下文,因此在if塊中,會產生一個新的上下文......而這恰好是上一級上下文的副本。換句話說,在if或else塊中,上下文與上一級上下文是相同的。可是當在一個each循環中使用if塊時就有必要細究一下了。在{{#each tours}}循環體中,可使用../.訪問上級上下文。不過,在{{#if ../currencies}}
塊中,又進入了一個新的上下文......因此要得到currency對象,就得使用../../.。第一個../得到產品的上下文,第二個得到最外層的上下文。這就會產生不少混亂,最簡單的權宜之計就是在each塊中避免使用if塊。服務器
在if和each塊中都有一個可選的else塊(對於each,若是數組中沒有任何元素,else塊就會執行)。咱們也用到了unless輔助方法,它基本上和if輔助方法是相反的:只有在參數爲false時,它纔會執行。
最後要注意的一點是在{{#each currencies}}塊中使用{{.}}。{{.}}指向當前上下文,在這個例子中,當前上下文只是咱們想打印出來的數組中的一個字符串。
訪問當前上下文還有另一種獨特的用法:它能夠從當前上下文的屬性中區分出輔助方法。例如,若是有一個輔助方法叫做foo
,在當前上下文中有一個屬性也叫做foo
,則{{foo}}
指向輔助方法,{{./foo}}
指向屬性。
服務器端模板與客戶端模板不一樣,客戶端模板可以經過查看HTML源文看到,而不會看到服務器端模板,或是用於最終生成HTML的上下文對象。
服務器端模板除了隱藏實現細節,還支持模板緩存,這對性能很重要。模板引擎會緩存已編譯的模板(只有在模板發生改變的時候纔會從新編譯和從新緩存),這會改進模板視圖的性能。默認狀況下,視圖緩存會在開發模式下禁用,在生產模式下啓用。若是想顯式地啓用視圖緩存,能夠這樣作:app.set('view cache', true)
Express支持Jade、EJS和JSHTML。因此須要添加一個node包,讓Express提供Handlebars支持。
npm install --save express3-handlebars
而後就能夠在Express中引入:
var handlebars = require('express3-handlebars').create({ defaultLayout: 'main' }); app.engine('handlebars', handlebars.engine); app.set('view engine', 'handlebars');
express3-handlebars讓Handlebars模板擁有了.handlebars擴展名。能夠在建立express3-handlebats實例將擴展名改爲一樣常見的.hbs;默認模版仍是main.hbs
var exphbs = require('express3-handlebars'); app.engine('hbs', exphbs({extname: '.hbs'})); app.set('view engine', 'hbs');