• 模板语言-Handlebars


    Handlebars 是什么

    Handlebars 是一种简单的模板语言。

    它使用模板与传入的对象来生成HTML 或者其他文本格式

    Handlebars 模板看起来像是嵌入了handlebars 表达式的普通文本。

    <p> {{firstname}} {{lastname}}p>
    一个handlebars表达式是使用两对尖括号包裹一些内容:{{some contet}}。当模板被执行时,表达式中的内容将被来自输入的对象中的值所替换。

    引入

    要使用Handlebars,首先download handlebars.js,然后再页面引入

    1. <script src="script/lib/jquery.js">script>
    2. <script src="script/lib/handlebars.js">script>

    有时,输入对象包含其他对象或数组。例如:

    在这种情况下,你可以使用点符号来访问嵌套属性:

    template

    {{person.firstname}} {{person.lastname}}

    一些内置的助手代码允许你将当前上下文更改为嵌套对象。然后,你就可以像访问根对象一样访问该对象了。

    #计算上下文

    内置的块助手代码 eachwith 允许你更改当前代码块的值。

    with 助手代码注入到对象的属性中,使你可以访问其属性。

    template

    1. {{#with person}}
    2. {{firstname}} {{lastname}}
    3. {{/with}}

    input

    1. {
    2. person: {
    3.   firstname: "Yehuda",
    4.   lastname: "Katz",
    5. },
    6. }

    each 助手代码会迭代一个数组,使你可以通过 Handlebars 简单访问每个对象的属性表达式。

    template

    1. <ul class="people_list">
    2. {{#each people}}
    3.   <li>{{this}}li>
    4. {{/each}}
    5. ul>
    input
    1.  { people: [  
    2. "Yehuda Katz",  
    3. "Alan Johnson",  
    4. "Charles Jolley",
    5. ],}

    #模板注释

    你可以像在其他语言的代码中一样在 Handlebars 代码中使用注释。由于 Handlebars 代码中通常存在一定程度的逻辑,因此这是一个好 习惯。

    注释将不会出现在结果输出中。如果你想显示注释。只需使用 html 注释。它们将被输出。

    任何包含 }} 或其他 Handlebars 标记的注释都应该使用 {{!--}} 语法。

    template

    1. {{! This comment will not show up in the output}}
    2. {{!-- This comment may contain mustaches like }} --}}

    #自定义助手

    通过调用 Handlebars.registerHelper 方法,你可以从模板中的任何上下文中访问 Handlebars 助手代码。

    template

    {{firstname}} {{loud lastname}}

    preparationScript

    1. Handlebars.registerHelper('loud', function (aString) {
    2.   return aString.toUpperCase()
    3. })

    助手代码将当前上下文作为函数的 this 指针接收。

    template

    1. {{#each people}}
    2.   {{print_person}}
    3. {{/each}}

    preparationScript

    1. Handlebars.registerHelper('print_person', function () {
    2.   return this.firstname + ' ' + this.lastname
    3. })

    块助手代码

    代码块表达式使你可以自定义这样的助手代码:这个助手代码可以使用与当前上下文不同的上下文来调用模板。这些块助手代码在名称前 以 # 号标识,并且需要一个名称相同的结束模板 /。让我们考虑一个生成 HTML 列表的助手代码:

    preparationScript

    1. Handlebars.registerHelper("list", function(items, options) {
    2. const itemsAsHtml = items.map(item => "
    3. " + options.fn(item) + "
    4. ");
  • return "
      \n" + itemsAsHtml.join("\n") + "\n
    "
    ;
  • });
  • 这个示例创建了一个名为 list 的助手代码来生成我们的 HTML 列表。助手代码接收一个 people 参数和一个 options 参数 。options 包含一个名为 fn 的属性,这个属性使你能够像调用普通的 Handlebars 模板一样调用代码块的上下文。

    执行后,模板将渲染:

    output

    1. <ul>
    2. <li>Yehuda Katzli>
    3. <li>Carl Lercheli>
    4. <li>Alan Johnsonli>
    5. ul>

    块助手代码具有其他功能,例如能够创建 else (例如,由内置的 if 函数使用)

    因为调用 options.fn(context) 时 Handlebars 会转义块助手代码的内容,因此 Handlebars 不会转义块助手代码的返回结果。如果 这样做,代码块内部的内容将被两次转义。

    表达式

    Handlebars 表达式是 Handlebars 模板的基本单位。 您可以在 {{mustache}} 中单独使用它们或将它们用作 Handlebars 助手代码, 或将其用作 Hash 参数中的值。

    #基本用法

    Handlebars 表达式是一些以双花括号 {{}} 括起来的内容。在以下的模版中,firstname 是一个被声明为表达式的变量,且被双花 括号括起来。

    template

    {{firstname}} {{lastname}}

    如果将以下对象输入模板:

    input

    1. {
    2. firstname: "Yehuda",
    3. lastname: "Katz",
    4. }

    表达式会编译为如下输出:

    output

    Yehuda Katz

    #路径表达式

    Handlebars 表达式亦可为以句点分隔的路径。

    template

    {{person.firstname}} {{person.lastname}}

    这个表达式将会在输入对象中查找 person 属性,然后查找 person 对象中的 firstnamelastname属性。 person 对象内的 属性。

    将如下输入传入模版:

    input

    1. {
    2. person: {
    3.   firstname: "Yehuda",
    4.   lastname: "Katz",
    5. },
    6. }

    会获得如下输出:

    output

    Yehuda Katz

    Handlebars 同时支持一个已弃用的 / 语法,因此你可以将上面的例子这样写:

    template

    {{person/firstname}} {{person/lastname}}

    #更改上下文

    一些诸如 #with and #each 的助手代码使你能够操作嵌套的对象。当你在路径中包含 ../ 时,Handlebars 将转回父级上下文。

    template

    1. {{#each people}}
    2.   {{../prefix}} {{firstname}}
    3. {{/each}}

    即使在注释的上下文中输出了名称,它仍然可以返回到主上下文(根对象)以检索前缀。

    注意

    ../ 解析的确切值根据调用该代码块的助手代码不同而有所不同。仅在上下文更改必要时使用 ../。诸如 {{#each}} 之类的子助 手代码将需要使用 ../ ,而诸如 {{#if}} 之类的助手代码则不需要。

    1. {{permalink}}
    2. {{#each comments}}
    3.  {{../permalink}}
    4.  {{#if title}}
    5.    {{../permalink}}
    6.  {{/if}}
    7. {{/each}}

    在此示例中,以上所有引用相同的前缀值,即使它们位于不同的块中也是如此。这样的行为是从 Handlebars 4 开始的, 发行说明 也讨论了先前的行为作为迁移计划。 Handlebars 还允许通过 this 来解决助手和数据字段之间的名称冲突。参考:

    #文字

    除了以下字符,标识符可以是任何 Unicode 文本:

    Whitespace ! " # % & ' ( ) * + , . / ; < = > @ [ \ ] ^ ``{|}~`

    除此之外,true, false, nullundefined 只允许在路径表达式的开头出现。

    若想引用一个并非合法的标识符,你可以使用 [。在路径表达式中你不必使用 ] 来关闭它,但其他表达式中是需要的。

    JavaScript 样式的字符串如 "' 也可用于替代 [

    template

    1. {{!-- wrong: {{array.0.item}} --}}
    2. correct: array.[0].item: {{array.[0].item}}
    3. {{!-- wrong: {{array.[0].item-class}} --}}
    4. correct: array.[0].[item-class]: {{array.[0].[item-class]}}
    5. {{!-- wrong: {{./true}}--}}
    6. correct: ./[true]: {{./[true]}}

    #HTML 转义

    在 Handlebars 中,由 {{expression}} 返回的值是 HTML 转义的。也就是说,如果一个表达式包含 &,那么返回的 HTML 转义的内 容将会包含 &。如果你不希望 Handlebars 转义字符的话,请使用 {{{

    在以下模板中,你将了解如何生成 HTML 转义的和原始的输出。

    template

    1. raw: {{{specialChars}}}
    2. html-escaped: {{specialChars}}

    将如下特殊的输入传入模版:

    input

    { specialChars: "& < > \" ' ` =" }

    使用 {{{ 会输出原始结果。否则将会输出 HTML 转义之后的结果,如下面的例子所示。

    output

    1. raw: & < > " ' ` =
    2. html-escaped: & < > " ' ` =

    #助手代码

    助手代码可以实现一些并非 Handlesbars 语言本身的功能。

    在运行时可以用 HandleBars.registerHelper 可以注册助手代码。例如为了将字符串中的所有字符转换为大写。

    preparationScript

    1. Handlebars.registerHelper('loud', function (aString) {
    2.   return aString.toUpperCase()
    3. })

    Handlebars 助手代码的调用需要一个简单标识符,且可紧接一个或多个参数(以空格分割)。每一参数为一个 Handlebars 表达式,且 将会用于上方“基本用法”中相同的方法来计算。

    template

    {{firstname}} {{loud lastname}}

    此例子中,load 是助手代码的名称,而 lastname 为传递给助手代码的参数。此模板,将会将输入的 uppercase 属性正确地转换 为大写:

    input

    1. {
    2. firstname: "Yehuda",
    3. lastname: "Katz",
    4. }

    output

    Yehuda KATZ

    #避免助手代码的返回值被 HTML 转义

    即使当使用 {{ 而非 {{{ 来调用助手代码时,当你的助手代码返回一个 Handlebars.Safestring 的实例,返回值也并不会被转义 。你需要留心将所有参数正确地使用 Handlebars.escapeExpression 来转义。

    preparationScript

    1. Handlebars.registerHelper("bold", function(text) {
    2. var result = "" + Handlebars.escapeExpression(text) + "";
    3. return new Handlebars.SafeString(result);
    4. });

    这里的单引号被转义

    #具有多个参数的助手代码

    我们观察一下另一个具有两个参数的助手代码:

    template

    {{link "See Website" url}}

    此例子中,Handlebars 将把两个参数传递给 link 助手代码:字符串 See Website 与从下面提供的 people 输入对象中的 people.value

    input

    { url: "https://yehudakatz.com/" }

    如同代码中所述,此助手代码 link 用于生成一个超链接。

    preparationScript

    1. Handlebars.registerHelper("link", function(text, url) {
    2.     var url = Handlebars.escapeExpression(url),
    3.         text = Handlebars.escapeExpression(text)
    4.          
    5.     return new Handlebars.SafeString("" + text +"");
    6. });

    我们会从上面的输入获得如下输出:

    output

    'https://yehudakatz.com/'>See Website

    在此例中,你可以使用同一助手代码,但使用基于 people.text 的值的动态文本:

    template

    {{link people.text people.url}}

    input

    1. {
    2. people: {
    3.   firstname: "Yehuda",
    4.   lastname: "Katz",
    5.   url: "https://yehudakatz.com/",
    6.   text: "See Website",
    7. },
    8. }

    #字面量参数

    帮助代码调用亦可含字面量,作为参数抑或是 Hash 参数。支持的字面量有数字、字符串、true, false, nullundefined

    template

    1. {{progress "Search" 10 false}}
    2. {{progress "Upload" 90 true}}
    3. {{progress "Finish" 100 false}}

    #含有 Hash 参数的助手代码

    Handlebars 提供了额外的元数据,例如 Hash 参数来作为助手代码的最后一个参数。

    template

    {{link "See Website" href=person.url class="person"}}

    在此模版中,最后一个参数 href=people.url class="people" 为传送至助手代码的 Hash 参数。

    Hash 参数中的键必须为简单标识符,且值为 Handlebars 表达式。这意味着值可以为简单标识符,路径或字符串。

    若将如下输入传入模版,其中 person.url 的值可以从 person 中获取。

    input

    1. {
    2. person: {
    3.   firstname: "Yehuda",
    4.   lastname: "Katz",
    5.   url: "https://yehudakatz.com/",
    6. },
    7. }

    正如以下助手代码中的描述,Hash 参数可以从最后一个参数 options 获取,以用于助手代码内部的进一步处理。

    preparationScript

    1. Handlebars.registerHelper("link", function(text, options) {
    2.   var attributes = [];
    3.   Object.keys(options.hash).forEach(key => {
    4.       var escapedKey = Handlebars.escapeExpression(key);
    5.       var escapedValue = Handlebars.escapeExpression(options.hash[key]);
    6.       attributes.push(escapedKey + '="' + escapedValue + '"');
    7.   })
    8.   var escapedText = Handlebars.escapeExpression(text);
    9.    
    10.   return new Handlebars.SafeString(escapedOutput);
    11. });

    上述助手代码产生的输出如下:

    output

    Handlebars 亦提供了使用一个模版块来调用助手代码的机制。块助手代码可用于执行任意次数(包括零次)的代码块并且使用它所选择 的任意上下文。

    了解更多:块助手代码

    #助手代码和属性查找时的消歧义

    如果助手代码注册时的名称和一个输入的属性名重复,则助手代码的优先级更高。如果你想使用输入的属性,请在其名称前加 ./this.。(或是已弃用的 this/。)

    template

    1. helper: {{name}}
    2. data: {{./name}} or {{this/name}} or {{this.name}}

    input

    { name: "Yehuda" }

    preparationScript

    1. Handlebars.registerHelper('name', function () {
    2.   return "Nils"
    3. })

    #子级表达式

    Handlebars 对子级表达式提供了支持,这使你可以在单个 Mustache 模板中调用多个助手代码,并且将内部助手代码调用的返回值作为 外部助手代码的参数传递。子级表达式使用括号定界。

    {{outer-helper (inner-helper 'abc') 'def'}}

    上例中,inner-helper 会被调用并带有字符串参数 'abc',同时不论 inner-helper 返回了什么,返回值都将被作为第一个参数 传递给 outer-helper(同时 'def' 会作为第二个参数传递)。

    #空格控制

    通过在括号中添加一个 ~ 字符,你可以从任何 Mustache 模板代码块的任何一侧省略模板中的空格。应用之后,该侧的所有空格将被 删除,直到第一个位于同一侧的 Handlebars 表达式或非空格字符出现。

    1. {{#each nav ~}}
    2.  <a href="{{url}}">
    3.    {{~#if test}}
    4.      {{~title}}
    5.    {{~^~}}
    6.     Empty
    7.    {{~/if~}}
    8.  a>
    9. {{~/each}}

    以及如下内容:

    1. {
    2.  nav: [{ url: "foo", test: true, title: "bar" }, { url: "bar" }];
    3. }

    输出没有换行符并格式化了空格的结果:

    <a href="foo">bara><a href="bar">Emptya>

    这扩展了「独立」助手代码(块助手代码、注释、代码片段或是空白符)在换行符时的默认行为。

    1. {{#each nav}}
    2.  <a href="{{url}}">
    3.    {{#if test}}
    4.      {{title}}
    5.    {{^}}
    6.     Empty
    7.    {{/if}}
    8.  a>
    9. {{~/each}}

    将会渲染

    1. <a href="foo">
    2. bar
    3. a>
    4. <a href="bar">
    5. Empty
    6. a>

    #转义 Handlebars 表达式

    Handlebars 可以从这两种方式中的任何一种转义:「内联转义」或「RAW 块助手代码」。内联转义通过 Mustache 代码块前置 \ 实现 ,而 RAW 代码块通过使用 {{{{ 实现。

    1. \{{escaped}}
    2. {{{{raw}}}}
    3.  {{escaped}}
    4. {{{{/raw}}}}

    RAW 代码块的操作方式与其他 块助手代码 均相同,但区别在于它的子内容被 Handlebars 视为一段字符串。

    #代码片段

    Handlebars 允许代码片段的复用。代码片段是一段普通的 Handlebars 模板,但它们可以直接由其他模板调用。

    #基本代码片段

    一个代码片段必须通过 Handlebars.registerPartial 注册。

    preparationScript

    Handlebars.registerPartial('myPartial', '{{prefix}}');

    这个方法将注册代码片段 myPartial。可以对代码片段进行预编译,并将预编译的模板传到第二个参数。

    调用代码片段是通过「代码片段调用语法」完成的:

    template

    {{> myPartial }}

    将渲染名为 myPartial 的代码片段。当代码片段执行时,它会在当前的代码块的上下文中运行。

    #动态代码片段

    使用子表达式语法可以动态选择要执行的部分。

    template

    这将计算 whichPartial,然后渲染以函数的返回值作为名称的代码片段。

    子表达式不会解析变量,因此 whichPartial 必须是一个函数。如果代码片段的名称是储存在一个变量里面的,则可以通过 lookup 助手代码来解决它。

    template

    {{> (lookup . 'myVariable') }}

    #代码片段上下文

    通过将上下文传递给代码片段,你可以在自定义上下文中执行代码片段。

    template

    {{> myPartial myOtherContext }}

    #代码片段参数

    可以通过 Hash 参数将自定义数据传递到代码片段。

    template

    {{> myPartial parameter=favoriteNumber }}

    代码片段运行时会将参数设置为 value

    这对于把数据从父级传递到代码片段的上下文中的时候特别有用:

    template

    #代码片段代码块

    一般来讲,尝试渲染一个未注册的代码片段会抛出错误。如果需要阻止错误抛出,可以在代码块中嵌套代码片段。

    template

    1. {{#> myPartial }}
    2. Failover content
    3. {{/myPartial}}

    如果代码片段 myPartial 尚未注册,则 Failover content 将被渲染。

    这种代码块的语法也可以用于将模板传递到代码片段中。有专门的代码片段执行此操作:@partial-block。考虑如下模板:

    template

    1. {{#> layout }}
    2. My Content
    3. {{/layout}}

    layout 代码片段包含

    partial: layout

    Site Content {{> @partial-block }}

    这将渲染:

    output

    Site Content My Content

    当以这种方式调用时,代码块将在 调用时代码片段中的上下文 中执行。此时深度路径查询和代码块参数是相对于代码片段的,而非 代码片段的模板。

    template

    这将渲染模板中的 person.firstname 而非代码片段。

    #内联代码片段

    模板可以通过 inline 修饰符定义代码块范围之内的代码片段。

    template

    这将为每个 child 渲染 myPartial

    每个内联代码片段均可用于当前代码块和所有子代码块(包括代码片段)。这使得「布局模板」和其他类似的功能成为可能:

    template

    1. {{#> layout}}
    2. {{#*inline "nav"}}
    3.   My Nav
    4. {{/inline}}
    5. {{#*inline "content"}}
    6.   My Content
    7. {{/inline}}
    8. {{/layout}}

    其中 layout 可能是:

    partial: layout

    1. class="nav">
    2. {{> nav}}
  • <div class="content">
  • {{> content}}
  • div>
  • 实例展示:

    代码:

    1. DOCTYPE html>
    2. <html lang="en">
    3. <head>
    4. <script src="./static/js/jquery-3.6.0.min.js">script>
    5. <script src="https://cdn.bootcdn.net/ajax/libs/handlebars.js/4.7.7/handlebars.js">script>
    6. head>
    7. <body>
    8. <main class="parent">
    9. <script id="epidemic_template" type="text/x-handlebars-template">
    10. <h2>最简单表达式 h2>
    11. <div>
    12. <p>{{firstname}} {{loud lastname}}p>
    13. div>
    14. <h2>列表h2>
    15. <ul class="people_list">
    16. {{#each people}}
    17. <li>{{this}}li>
    18. {{/each}}
    19. ul>
    20. <h2>列表写法2h2>
    21. {{#list people2}}
    22. {{firstname2}} {{lastname2}}
    23. {{/list}}
    24. <h2>嵌套助手h2>
    25. {{outer-helper (inner-helper "abc") "def"}}
    26. <h2>whith助手h2>
    27. <div class="entry">
    28. <h3>{{title}}h3>
    29. {{#with story}}
    30. <div class="intro">{{{intro}}}div>
    31. <div class="body">{{{body}}}div>
    32. {{/with}}
    33. div>
    34. <h2>具多个参数的助手h2>
    35. {{link "See Website" url}}
    36. {{link people3.text people3.url}}
    37. <div>
    38. {{#each nav~}}
    39. <a href="{{url}}">
    40. {{~#if test}}
    41. {{~title}}
    42. {{~else~}}
    43. Empty
    44. {{~/if~}}
    45. a>
    46. {{~/each}}
    47. div>
    48. <div>
    49. <div>
    50. raw:
    51. {{{specialChars}}}
    52. div>
    53. <div>
    54. html-escaped:
    55. {{specialChars}}
    56. div>
    57. div>
    58. <h1>转义\{{}}表达式的两种方式 raw 可以用代码助手
    59. 区别是返回的永远是一串字符春h1>
    60. <div>
    61. \{{escaped}}
    62. {{{{raw}}}}
    63. {{escaped}}
    64. {{{{/raw}}}}
    65. {{{{raw}}}}
    66. {{这个会被转义 不会当作是表达式 会被当成正常的字符串 hallo world}}
    67. {{{{/raw}}}}
    68. div>
    69. script>
    70. <script>