如何解决如何将 Underscore 模板转换为 Handlebars 模板?
在(旧)购物车页面中是“运费估算器”的代码,他们希望在新主题中重新使用(因为它运行良好)。 我已经复制了相关文件,但在执行并按下计算按钮时,我们得到以下显示:
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商家会很高兴;)
解决方法
通常,Underscore 和 Handlebars 并不是真正的替代品。 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 %>
将在文本中插入 JavaScriptexpression
的字符串值。这称为插值标记。 -
<%- expression %>
会做同样的事情,但 HTML 转义。 -
<% code %>
允许您编写任意 JavaScriptcode
,使模板的某些部分成为有条件的或重复的。通常,您会发现一个这样的标签类似于<% 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}}
将为...
的每个元素或属性重复name
。name
成为...
的上下文;也就是说,如果您在{{otherName}}
中写入...
,Handlebars 将尝试查找otherName
作为由name
标识的对象的属性。 -
{{#name}}...{{/name}}
是 Handlebars 继承自 Mustache 的符号。当{{#each name}}
是一个数组时,它的行为类似于name
,否则类似于{{#if name}}
(如果它是一个对象,也将上下文更改为name
)。这背后的想法(在 Mustache 中)是使模板更具声明性,或者作者称之为“无逻辑”。 - 还有更多的标签,我现在不会介绍。
将 Underscore 模板转换为 Handlebars
由于 Underscore 允许在模板中插入任意 JavaScript 代码,因此并不总是可以将 Underscore 模板转换为等效的 Handlebars 模板。然而,幸运的是,模板并不真正需要 JavaScript 的全部表达能力,因此 Underscore 模板很可能以一种可以移植到限制性更强的模板语言的方式编写(“幸运的是” ”)。如果可能,以下策略在大多数情况下就足够了:
- 将出现在任何
print(_.escape(expression))
标签内的任何<%...%>
替换为%><%- expression %><%
。 - 将
print(expression)
中出现的任何其他<%...%>
替换为%><%= expression %><%
。 - 用
<%- expression %>
替换所有出现的{{expression}}
(包括第 1 步之前已经存在的)。 - 用
<%= expression %>
替换所有出现的{{{expression}}}
(包括第 2 步之前已经存在的)。 - 如果您在任何地方(
var name = otherName.propertyName
语句的括号内除外)找到for (...)
形式的别名,请将otherName.propertyName
替换为name
此变量在范围内的任何地方并删除该变量。 不要对for (...)
内的循环变量执行此操作;这些在第 7 步中被替换。 - 如果您发现
<% 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}}
。这一次,最后一个<% } %>
消失了。
- 将最后的
- 替换循环,再次从最里面的表达式开始,然后从那里开始:
- 用符号
<% _.each(objectName,function(valueName,keyName,collectionName) { %>...<% }) %>
替换{{#each objectName}}...{{/each}}
形式的对象上的函数循环。检查{{}}
中嵌套的{{{}}}
/{{#if}}
/{{#each}}
/...
标签是否出现keyName
、collectionName
或 {{ 1}} 并将它们分别替换为objectName
、@key
和..
(这不是错字;..
和collectionName
都应该替换为 { {1}} 因为它们指的是同一个对象)。请注意,Underscore 版本中传递给objectName
的函数可能需要更少的参数,在这种情况下,..
甚至_.each
可能不存在;无论如何,替换的工作原理都是一样的。 - 用符号
collectionName
替换keyName
形式的数组上的函数循环。检查<% _.each(arrayName,indexName,collectionName) { %>...<% }) %>
中嵌套的{{#each arrayName}}...{{/each}}
/{{}}
/{{{}}}
/{{#if}}
标签是否出现{{#each}}
、...
或 {{ 1}} 并分别用indexName
、collectionName
和arrayName
替换它们。同样,在 Underscore 版本中传递给@index
的函数可能需要更少的参数;无论如何,替换的工作原理都是一样的。 - 用符号
..
替换..
形式的对象上的程序循环。检查_.each
中嵌套的<% for (var keyName in objectName) { %>...<% } %>
/{{#each objectName}}...{{/each}}
/{{}}
/{{{}}}
标签是否出现{{#if}}
、{{#each}}
或 {{ 1}} 并将它们分别替换为...
、keyName
和objectName[keyName]
。 - 用符号
objectName
替换@key
形式的数组上的程序循环。检查this
中嵌套的..
/<% for (var indexName = 0; indexName < arrayName.length; ++indexName) { %>...<% } %>
/{{#each arrayName}}...{{/each}}
/{{}}
标签是否出现{{{}}}
、{{#if}}
或 {{ 1}} 并将它们分别替换为{{#each}}
、...
和indexName
。
- 用符号
- 修正表达式语法:
- 如果您在上一步中创建了
arrayName[indexName]
形式的表达式,其中前两个句点arrayName
最初是一个名称(@index
或this
,如下所述步骤 7),将其替换为..
。您可能有更长的这种形式的路径,例如...propertyName
。 -
..
形式的子表达式应该变成objectName
(注意句点)。
- 如果您在上一步中创建了
- 检查您翻译的所有
arrayName
和../propertyName
是否都可以由 Handlebars 按原样评估。根据经验,Handlebars 只能直接评估(嵌套)当前上下文的属性名称(例如../../propertyName
或name[index1][index2]
)以及它识别的一些特殊符号,例如name.[index].[index2]
、expression
、condition
、keyName
和keyName.subProperty
。使用 helpers 来评估表达式,这些表达式不仅仅是@key
或@index
所必需的某些对象和锚名称的名称:- 请注意,
@root
仅相当于this
,因为..
指的是当前上下文。 - 当将
@root
之类的对象传递给模板时,始终可以使用..
、this.propertyName
等引用此最外层对象的属性。请注意,该对象可能在原始 Underscore 模板中已被赋予其自己的名称;在这种情况下,此名称本身可以替换为propertyName
。 -
this
用于在循环内引用父上下文,如我们在步骤 7-8 中所见。有时,原始 Underscore 模板中的循环可能会通过关闭来直接引用父上下文的属性;在这种情况下,您可以根据需要在此属性的名称前加上{a: foo,b: {c: baz}}
前缀,以帮助 Handlebars 找到正确的属性。
- 请注意,
- 在之前的转换之后,您可能有剩余的空
@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 举报,一经查实,本站将立刻删除。