express-8 Handlebars模板引擎(1)

时间:2022-08-24 09:02:51

简介

  • 使用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充满了问题:

  • 必须不断地考虑哪些字符需要转义以及如何转义。
  • 使用JavaScript来生成那些自身包含JavaScript代码的HTML会很快让你抓狂。
  • 通常会失去编辑器的语法高亮显示和其他方便的语言特性。
  • 很难发现格式不正确的HTML。
  • 很难直观地分析。
  • 很难让别人读懂你的代码。

模板解决了在目标语言中编写代码的问题,同时也让插入动态数据成为了可能; 只有在某些最简单的情况下才会使用JavaScript生成HTML。

选择模板引擎

一些可供参考的准则: 参考推荐文章

  • 性能: 希望模板引擎尽可能地快
  • 客户端、服务端或兼而有之: 大多数(但不是所有)模板引擎都可用于客户端和服务器端。如果需要在这两端都使用模板,推荐选择那些在两端都表现优秀的模板引擎。
  • 抽象: 让代码更可读(例如,在普通HTML文本中使用大括号)

Template-Engine-Chooser

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版本的最新版本,从而让你的内容更具“前瞻性”。

Handlebars基础

理解模板引擎的关键在于context(上下文环境):当渲染一个模板时,便会传递给模板引擎一个对象,叫作上下文对象,它能让替换标识运行。

例如,如果上下文对象是{ name: 'Buttercup' },模板是<p>Hello, {{name}}!</p> ,则 {{name}}会被Buttercup替换。如果向模板中传递HTML文本会发生什么呢?例如,上下文换成{ name: '<b>Buttercup</b>' },使用之前的模板得到的结果将是<p>Hello,&lt;b&gt;Buttercup&lt;b&gt;</p>,这或许并不是你想要的。要想解决这个问题,用三个大括号代替两个就可以了:{{{name}}}。

express-8 Handlebars模板引擎(1)

  • 注释: 区分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');