微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

如何将 Underscore 模板转换为 Handlebars 模板?

如何解决如何将 Underscore 模板转换为 Handlebars 模板?

我正在升级使用旧主题的 Shopify 商店。

在(旧)购物车页面中是“运费估算器”的代码,他们希望在新主题中重新使用(因为它运行良好)。 我已经复制了相关文件,但在执行并按下计算按钮时,我们得到以下显示

class="success" class="error" > 1) { %> 有 的可用运费,从 。 ....

这来自以下代码

<script id="shipping-calculator-response-template" type="text/template">
  <p id="shipping-rates-Feedback" <% if (success) { %> class="success" <% } else { %> class="error" <% } %>>
  <% if (success) { %>
    <% if (rates.length > 1) { %> 
    There are <%= rates.length %> shipping rates available for <%= address %>,starting at <%= rates[0].price %>.
    <% } else if (rates.length == 1) { %>
    ... 
</script>

所以,我猜脚本没有被识别/处理为“文本/模板”

主题包含对以下内容的引用:

<script src="//cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.0.10/handlebars.min.js"></script>

和旧主题

<script src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.6.0/underscore-min.js" type="text/javascript"></script>

所以我注释掉了handlebar,并替换为下划线。但结果还是一样。

我是在正确的轨道上,还是以上无关紧要?

我需要从 Underscore 解密的完整代码 - 并为 HandleBars 重新编码如下:

<script id="shipping-calculator-response-template" type="text/template">
  <p id="shipping-rates-Feedback" <% if (success) { %> class="success" <% } else { %> class="error" <% } %>>
  <% if (success) { %>
    <% if (rates.length > 1) { %> 
    There are <%- rates.length %> shipping rates available for <%- address %>,starting at <%= rates[0].price %>.
    <% } else if (rates.length == 1) { %>
    There is one shipping rate available for <%- address %>.
    <% } else { %>
    We do not ship to this destination.
    <% } %>
  <% } else { %>
    <%- errorFeedback %>
  <% } %>
  </p>
  <ul id="shipping-rates">
    <% for (var i=0; i<rates.length; i++) { %>
    <li><%- rates[i].name %> at <%= rates[i].price %></li>
    <% } %>
  </ul> 
</script>

如果我们能做到这一点,很多shopify商家会很高兴;)

解决方法

通常,UnderscoreHandlebars 并不是真正的替代品。 Underscore 是一个具有通用功能实用程序的工具包,可帮助您以函数式风格编写更短、更易于维护的代码。另一方面,Handlebars 是一个完全致力于模板渲染的库,可帮助您编写更清晰、更易于维护的模板。

使用 Underscore 时,您可能会发现它的函数在整个 JavaScript 代码中的任何地方都被调用,而 Handlebars 仅在您将渲染模板的地方被调用。出于这个原因,这些库通常根本不冲突;完全有可能编写一个依赖于两者的应用程序(实际上我在我的大多数应用程序中已经这样做了一段时间)。只需将两个库链接到您的页面,

<script src="https://cdn.jsdelivr.net/npm/underscore@1.12.0/underscore-min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/handlebars@4.7.7/dist/handlebars.js"></script>

或将两者作为模块导入,

import _ from 'underscore';
import * as Handlebars from 'handlebars';

或对旧的模块格式(例如 AMD 或 CommonJS)执行相同操作。

但是,Underscore 确实有一个 template 函数,这是一个非常小的模板实现,可以用作像 Handlebars 这样的库的替代方案。在旧版本的应用程序中似乎就是这种情况,并且会导致冲突,因为 Underscore 的最小模板语言与 Handlebars 的模板语言不同。这些库之间的编译和渲染步骤也略有不同。

比较模板语言

这两种模板语言都允许您在一段文本中插入特殊标签,以使该文本的一部分具有参数化、条件化或重复性。然而,它们支持的标签是不同的。

下划线:

  • <%= expression %> 将在文本中插入 JavaScript expression 的字符串值。这称为插值标记。
  • <%- expression %> 会做同样的事情,但 HTML 转义。
  • <% code %> 允许您编写任意 JavaScript code,使模板的某些部分成为有条件的或重复的。通常,您会发现一个这样的标签类似于 <% for (...) { %>,然后在模板的更深处,有一个匹配的 <% } %>。这两个代码标记之间的模板部分就是一个循环,它将按照 for 的逻辑重复。同样,您可能会发现 <% if (...) { %>...<% } %> 使模板的 ... 部分成为条件。 (老实说,这很丑陋,但它有助于实现最小化。Underscore 的 template module 只有一页。)
  • code<% code %> 部分中,您偶尔会发现 print(expression)。这是一种速记,目的是避免跳出代码标签,插入带有 expression 的插值标签,然后立即使用新的代码标签继续。换言之,<% code1 print(expression) code2 %><% code1 %><%= expression %><% code2 %> 的简写。

车把:

  • {{name}} 在模板中插入键为 name 的属性的 HTML 转义字符串值。
  • {{{name}}} 做同样的事情,但没有 HTML 转义。
  • {{#if condition}}...{{/if}} 仅在满足 ... 时才会插入 condition 部分。
  • {{#each name}}...{{/each}} 将为 ... 的每个元素或属性重复 namename 成为 ...上下文;也就是说,如果您在 {{otherName}} 中写入 ...,Handlebars 将尝试查找 otherName 作为由 name 标识的对象的属性。
  • {{#name}}...{{/name}} 是 Handlebars 继承自 Mustache 的符号。当 {{#each name}} 是一个数组时,它的行为类似于 name,否则类似于 {{#if name}}(如果它是一个对象,也将上下文更改为 name)。这背后的想法(在 Mustache 中)是使模板更具声明性,或者作者称之为“无逻辑”。
  • 还有更多的标签,我现在不会介绍。

将 Underscore 模板转换为 Handlebars

由于 Underscore 允许在模板中插入任意 JavaScript 代码,因此并不总是可以将 Underscore 模板转换为等效的 Handlebars 模板。然而,幸运的是,模板并不真正需要 JavaScript 的全部表达能力,因此 Underscore 模板很可能以一种可以移植到限制性更强的模板语言的方式编写(“幸运的是” ”)。如果可能,以下策略在大多数情况下就足够了:

  1. 将出现在任何 print(_.escape(expression)) 标签内的任何 <%...%> 替换为 %><%- expression %><%
  2. print(expression) 中出现的任何其他 <%...%> 替换为 %><%= expression %><%
  3. <%- expression %> 替换所有出现的 {{expression}}(包括第 1 步之前已经存在的)。
  4. <%= expression %> 替换所有出现的 {{{expression}}}(包括第 2 步之前已经存在的)。
  5. 如果您在任何地方(var name = otherName.propertyName 语句的括号内除外)找到 for (...) 形式的别名,请将 otherName.propertyName 替换为 name此变量在范围内的任何地方并删除该变量。 不要for (...) 内的循环变量执行此操作;这些在第 7 步中被替换。
  6. 如果您发现 <% if (condition1) { %>...<% } else if (condition2) { %>...<% } else ... if (conditionN) { %>...<% } else { %>...<% } %> 形式的任何模式,请从最后一个最里面的块开始,然后从那里向外工作,如下所示:
    • 将最后的 <% } else { %> 替换为 {{else}}(Handlebars 将其识别为特殊符号)。
    • 将最后的中间 <% } else if (conditionN) { %>...<% } %> 替换为 {{else}}{{#if conditionN }}...{{/if}}<% } %>。重复此步骤直到没有更多的 else if。请注意,最后的 <% } %> 保持原位;您为每个中间 {{/if}} 在它前面插入一个额外的 else if
    • 将最外面的 <% if (condition1) { %>...<% } %> 替换为 {{#if condition1}}...{{/if}}。这一次,最后一个 <% } %> 消失了。
  7. 替换循环,再次从最里面的表达式开始,然后从那里开始:
    • 用符号 <% _.each(objectName,function(valueName,keyName,collectionName) { %>...<% }) %> 替换 {{#each objectName}}...{{/each}} 形式的对象上的函数循环。检查 {{}} 中嵌套的 {{{}}}/{{#if}}/{{#each}}/... 标签是否出现 keyNamecollectionName 或 {{ 1}} 并将它们分别替换为 objectName@key..(这不是错字;..collectionName 都应该替换为 { {1}} 因为它们指的是同一个对象)。请注意,Underscore 版本中传递给 objectName 的函数可能需要更少的参数,在这种情况下,.. 甚至 _.each 可能不存在;无论如何,替换的工作原理都是一样的。
    • 用符号 collectionName 替换 keyName 形式的数组上的函数循环。检查 <% _.each(arrayName,indexName,collectionName) { %>...<% }) %> 中嵌套的 {{#each arrayName}}...{{/each}}/{{}}/{{{}}}/{{#if}} 标签是否出现 {{#each}}... 或 {{ 1}} 并分别用 indexNamecollectionNamearrayName 替换它们。同样,在 Underscore 版本中传递给 @index 的函数可能需要更少的参数;无论如何,替换的工作原理都是一样的。
    • 用符号 .. 替换 .. 形式的对象上的程序循环。检查 _.each 中嵌套的 <% for (var keyName in objectName) { %>...<% } %>/{{#each objectName}}...{{/each}}/{{}}/{{{}}} 标签是否出现 {{#if}}{{#each}} 或 {{ 1}} 并将它们分别替换为 ...keyNameobjectName[keyName]
    • 用符号 objectName 替换 @key 形式的数组上的程序循环。检查 this 中嵌套的 ../<% for (var indexName = 0; indexName < arrayName.length; ++indexName) { %>...<% } %>/{{#each arrayName}}...{{/each}}/{{}} 标签是否出现 {{{}}}{{#if}} 或 {{ 1}} 并将它们分别替换为 {{#each}}...indexName
  8. 修正表达式语法:
    • 如果您在上一步中创建了 arrayName[indexName] 形式的表达式,其中前两个句点 arrayName 最初是一个名称(@indexthis,如下所述步骤 7),将其替换为 ..。您可能有更长的这种形式的路径,例如 ...propertyName
    • .. 形式的子表达式应该变成 objectName(注意句点)。
  9. 检查您翻译的所有 arrayName../propertyName 是否都可以由 Handlebars 按原样评估。根据经验,Handlebars 只能直接评估(嵌套)当前上下文的属性名称(例如 ../../propertyNamename[index1][index2])以及它识别的一些特殊符号,例如 name.[index].[index2]expressionconditionkeyNamekeyName.subProperty。使用 helpers 来评估表达式,这些表达式不仅仅是 @key@index 所必需的某些对象和锚名称的名称:
    • 请注意,@root 仅相当于 this,因为 .. 指的是当前上下文。
    • 当将 @root 之类的对象传递给模板时,始终可以使用 ..this.propertyName 等引用此最外层对象的属性。请注意,该对象可能在原始 Underscore 模板中已被赋予其自己的名称;在这种情况下,此名称本身可以替换为 propertyName
    • this 用于在循环内引用父上下文,如我们在步骤 7-8 中所见。有时,原始 Underscore 模板中的循环可能会通过关闭来直接引用父上下文的属性;在这种情况下,您可以根据需要在此属性的名称前加上 {a: foo,b: {c: baz}} 前缀,以帮助 Handlebars 找到正确的属性。
  10. 在之前的转换之后,您可能有剩余的空 @root.a 标签;这些可以安全地移除。

如果在执行上述步骤后,您的模板中仍有 @root.b.c 表示法,或 Handlebars 无法计算的表达式,则您可能需要使用 Handlebars 语言中的其他工具或创建特殊的解决方法。如果你很不走运,模板根本无法翻译,但大多数时候会有办法。

演示:您的模板

在此处重复您的问题中的下划线模板:

@root

遵循上述算法并使用表达式 .. 而不是 ../(因为 Handlebars 无法评估开箱即用的比较),我们成功获得了以下 Handlebars 模板:

<% %>

您可能还会发现其他需要翻译的模板。您可以对其他模板采用相同的方法。

最后一点:嵌入在 HTML 中的模板

您的主题在页面中包含具有以下符号的模板。

<% code %>

重要的是要认识到,虽然这是一个 <p id="shipping-rates-feedback" <% if (success) { %> class="success" <% } else { %> class="error" <% } %>> <% if (success) { %> <% if (rates.length > 1) { %> There are <%- rates.length %> shipping rates available for <%- address %>,starting at <%= rates[0].price %>. <% } else if (rates.length == 1) { %> There is one shipping rate available for <%- address %>. <% } else { %> We do not ship to this destination. <% } %> <% } else { %> <%- errorFeedback %> <% } %> </p> <ul id="shipping-rates"> <% for (var i=0; i<rates.length; i++) { %> <li><%- rates[i].name %> at <%= rates[i].price %></li> <% } %> </ul> 标签,但浏览器实际上并没有将内容解释为脚本。相反,因为标签有一个浏览器不知道的 rates.[1],浏览器只是将标签原样保留在 DOM 中并继续解释下一个元素。这是在 HTML 页面中嵌入任意文本数据的常用技巧,以便稍后由脚本获取而无需用户查看。在这种特殊情况下,您的 JavaScript 片段将在某处执行类似

rates.length > 1

然后将 <p id="shipping-rates-feedback" {{#if success}} class="success" {{else}} class="error" {{/if}}> {{#if success}} {{#if rates.[1]}} There are {{rates.length}} shipping rates available for {{address}},starting at {{{rates.[0].price}}}. {{else}}{{#if rates}} There is one shipping rate available for {{address}}. {{else}} We do not ship to this destination. {{/if}}{{/if}} {{else}} {{errorFeedback}} {{/if}} </p> <ul id="shipping-rates"> {{#each rates}} <li>{{name}} at {{{price}}}</li> {{/each}} </ul> 传递给 Handlebars 以进行处理。这也是为什么用 Underscore 替换 Handlebars 没有解决您的问题的原因;该脚本仍会尝试将模板传递给 Handlebars。最后,在您的特定情况下,可能没有必要放回 Underscore 引用。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。