express-8 Handlebars模板引擎(1)

簡介

  • 使用JavaScript生成一些HTML
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來生成那些自身包含JavaScript代碼的HTML會很快讓你抓狂。
  • 一般會失去編輯器的語法高亮顯示和其餘方便的語言特性。
  • 很難發現格式不正確的HTML。
  • 很難直觀地分析。
  • 很難讓別人讀懂你的代碼。

模板解決了在目標語言中編寫代碼的問題,同時也讓插入動態數據成爲了可能; 只有在某些最簡單的狀況下才會使用JavaScript生成HTML。node

選擇模板引擎

一些可供參考的準則: 參考推薦文章git

  • 性能: 但願模板引擎儘量地快
  • 客戶端、服務端或兼而有之: 大多數(但不是全部)模板引擎均可用於客戶端和服務器端。若是須要在這兩端都使用模板,推薦選擇那些在兩端都表現優秀的模板引擎。
  • 抽象: 讓代碼更可讀(例如,在普通HTML文本中使用大括號)

Template-Engine-Choosergithub

Jade

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

Handlebars基礎

理解模板引擎的關鍵在於context(上下文環境):當渲染一個模板時,便會傳遞給模板引擎一個對象,叫做上下文對象,它能讓替換標識運行。npm

例如,若是上下文對象是{ name: 'Buttercup' },模板是<p>Hello, {{name}}!</p> ,則 {{name}}會被Buttercup替換。若是向模板中傳遞HTML文本會發生什麼呢?例如,上下文換成{ name: '<b>Buttercup</b>' },使用以前的模板獲得的結果將是<p>Hello,&lt;b&gt;Buttercup&lt;b&gt;</p>,這或許並非你想要的。要想解決這個問題,用三個大括號代替兩個就能夠了:{{{name}}}。

  • 註釋: 區分Handlebars註釋和HTML註釋很重要。示例以下:
{{! 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');
相關文章
相關標籤/搜索