如何将 Underscore 模板转换为 Handlebars 模板?
How to translate an Underscore template to a Handlebars template?
我正在升级使用旧主题的 Shopify 商店。
在(旧的)购物车页面中是 'Shipping Estimator' 的代码,他们希望在新主题中重复使用该代码(因为它运行良好)。
我已经复制了相关文件,但在执行并按下计算按钮时,我们会显示以下内容:
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 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>
所以,我猜脚本并不是 recognise/treated 和 'text/template'
新主题包含对以下内容的引用:
<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。但是还是一样的结果。
我是在正确的轨道上,还是以上无关紧要?
我需要从 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 and Handlebars并不是真正的替代品。 Underscore 是一个具有通用函数式实用程序的工具包,可帮助您以函数式风格编写更短、更易于维护的代码。另一方面,Handlebars 是一个完全致力于模板渲染的库,可帮助您编写更简洁、更易于维护的模板。
使用 Underscore 时,您可能会发现它的函数在整个 JavaScript 代码中随处被调用,而 Handlebars 仅在您将呈现模板的地方被调用。因此,这些库通常根本不冲突;完全有可能编写一个依赖于两者的应用程序(事实上,我在我的大多数应用程序中已经这样做了一段时间)。只需 link 将两个库都放入您的页面,
<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)
。这是一个 shorthand 的意思是为了避免必须打破代码标签,插入一个带有 expression
的插值标签,然后立即使用新的代码标签恢复。换句话说,<% code1 print(expression) code2 %>
是 shorthand for <% code1 %><%= expression %><% code2 %>
.
车把:
{{name}}
在模板中插入 属性 的 HTML 转义字符串值,键为 name
。
{{{name}}}
做同样的事情,但是 没有 HTML-escaping.
{{#if condition}}...{{/if}}
将仅在满足 condition
时插入 ...
部分。
{{#each name}}...{{/each}}
将为每个元素重复 ...
或 name
的 属性。 name
成为 ...
的 上下文 ;也就是说,如果您在 ...
中写入 {{otherName}}
,Handlebars 将尝试将 otherName
查找为由 name
.[=268= 标识的对象的 属性 ]
{{#name}}...{{/name}}
是 Handlebars 继承自 Mustache 的符号。当 name
是一个数组时,它的行为类似于 {{#each 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
的别名(except 在 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
。请注意,最后的 <% } %>
保持不变 ;您要在它前面为每个中间 else if
. 插入一个额外的 {{/if}}
- 将最外面的
<% if (condition1) { %>...<% } %>
替换为 {{#if condition1}}...{{/if}}
。这一次,最后的 <% } %>
消失了。
- 替换循环,再次从最里面的表达式开始,然后从那里向外工作:
- 用符号
{{#each objectName}}...{{/each}}
替换 形式 <% _.each(objectName, function(valueName, keyName, collectionName) { %>...<% }) %>
对象 的功能循环。检查 ...
中嵌套的 {{}}
/{{{}}}
/{{#if}}
/{{#each}}
标签是否出现 keyName
、collectionName
或 objectName
并分别用 @key
、..
和 ..
替换它们(这不是错字;collectionName
和 objectName
都应替换为 ..
因为它们指的是同一个对象)。请注意,在 Underscore 版本中传递给 _.each
的函数可能需要更少的参数,在这种情况下 collectionName
甚至 keyName
可能不存在;无论如何,替换工作都是一样的。
- 用符号
{{#each arrayName}}...{{/each}}
替换 形式 <% _.each(arrayName, function(valueName, indexName, collectionName) { %>...<% }) %>
数组 上的函数循环。检查 ...
中嵌套的 {{}}
/{{{}}}
/{{#if}}
/{{#each}}
标签是否出现 indexName
、collectionName
或 arrayName
并分别用 @index
、..
和 ..
替换它们。同样,在 Underscore 版本中传递给 _.each
的函数可能需要更少的参数;无论如何,替换工作都是一样的。
- 用符号
{{#each objectName}}...{{/each}}
替换 对象 形式 <% for (var keyName in objectName) { %>...<% } %>
的程序循环。检查 ...
中嵌套的 {{}}
/{{{}}}
/{{#if}}
/{{#each}}
标签是否出现 keyName
、objectName[keyName]
或 objectName
并分别替换为 @key
、this
和 ..
。
- 用符号
{{#each arrayName}}...{{/each}}
替换 数组 形式 <% for (var indexName = 0; indexName < arrayName.length; ++indexName) { %>...<% } %>
的程序循环。检查 ...
中嵌套的 {{}}
/{{{}}}
/{{#if}}
/{{#each}}
标签是否出现 indexName
、arrayName[indexName]
或 arrayName
并分别替换为 @index
、this
和 ..
。
- 修复表达式语法:
- 如果您在上一步中创建了
...propertyName
形式的表达式,其中前两个句点 ..
最初是一个名称(objectName
或 arrayName
作为在步骤 7) 中有描述,用 ../propertyName
替换它。这种形式的路径可能更长,例如 ../../propertyName
.
name[index1][index2]
形式的子表达式应转换为 name.[index].[index2]
(注意句点)。
- 检查您翻译的所有
expression
和 condition
是否可以被 Handlebars 按原样评估。根据经验,Handlebars 只能直接评估(嵌套)属性 当前上下文的名称(例如 keyName
或 keyName.subProperty
)以及它识别的一些特殊符号,例如 [= 90=、@index
、@root
、this
和 ..
。使用 helpers 来评估不仅仅是某些对象名称的表达式和 @root
或 ..
所必需的锚点名称:
- 请注意
this.propertyName
等同于 propertyName
,因为 this
指的是当前上下文。
- 当向模板传递像
{a: foo, b: {c: baz}}
这样的对象时,这个最外层对象的属性总是可以用@root.a
、@root.b.c
等引用。请注意,这个对象可能在原始 Underscore 模板中被赋予了自己的名称;在这种情况下,此名称本身可以替换为 @root
.
..
用于在循环中引用父上下文,正如我们在步骤 7-8 中看到的那样。有时,原始 Underscore 模板中的循环可能会通过关闭直接引用父上下文的 属性 ;在这种情况下,您可以根据需要在 属性 的名称前加上 ../
来帮助 Handlebars 找到正确的 属性。
- 您可能在之前的转换后留下空的
<% %>
标签;这些可以安全地移除。
如果在执行上述步骤后,您的模板中仍有 <% code %>
符号,或 Handlebars 无法计算的表达式,您可能需要使用 Handlebars 语言的其他工具或创建特殊的解决方法。如果你很倒霉,模板根本无法翻译,但大多数时候会有办法的。
演示:您的模板
在此处重复您问题中的 Underscore 模板:
<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]
代替rates.length > 1
(因为Handlebars无法开箱即用地评估比较),我们成功获得了以下Handlebars模板:
<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>
您可能会发现其他需要翻译的模板。您可以对其他模板采用相同的方法。
最后说明:嵌入在 HTML
中的模板
您的主题在页面中包含带有以下符号的模板。
<script id="shipping-calculator-response-template" type="text/template">
// the template
</script>
重要的是要认识到,虽然这是一个 <script>
标记,但浏览器实际上并不将内容解释为脚本。相反,因为标签有一个浏览器不知道的 type
,所以浏览器只是将标签留在 DOM 中并继续解释下一个元素。这是在 HTML 页面中嵌入任意文本数据的常用技巧,这样它就可以在以后由脚本获取,而无需用户看到它。在这种特殊情况下,你的 JavaScript 某处会做一些类似
的事情
var templateText = document.querySelector('#shipping-calculator-response-template').textContent;
然后将 templateText
传递给 Handlebars 以便对其进行处理。这也是为什么用 Underscore 替换 Handlebars 没有解决你的问题的原因;该脚本仍会尝试将模板传递给 Handlebars。最后,在您的特定情况下,可能没有必要放回 Underscore 参考。
我正在升级使用旧主题的 Shopify 商店。
在(旧的)购物车页面中是 'Shipping Estimator' 的代码,他们希望在新主题中重复使用该代码(因为它运行良好)。 我已经复制了相关文件,但在执行并按下计算按钮时,我们会显示以下内容:
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 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>
所以,我猜脚本并不是 recognise/treated 和 'text/template'
新主题包含对以下内容的引用:
<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。但是还是一样的结果。
我是在正确的轨道上,还是以上无关紧要?
我需要从 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 and Handlebars并不是真正的替代品。 Underscore 是一个具有通用函数式实用程序的工具包,可帮助您以函数式风格编写更短、更易于维护的代码。另一方面,Handlebars 是一个完全致力于模板渲染的库,可帮助您编写更简洁、更易于维护的模板。
使用 Underscore 时,您可能会发现它的函数在整个 JavaScript 代码中随处被调用,而 Handlebars 仅在您将呈现模板的地方被调用。因此,这些库通常根本不冲突;完全有可能编写一个依赖于两者的应用程序(事实上,我在我的大多数应用程序中已经这样做了一段时间)。只需 link 将两个库都放入您的页面,
<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)
。这是一个 shorthand 的意思是为了避免必须打破代码标签,插入一个带有expression
的插值标签,然后立即使用新的代码标签恢复。换句话说,<% code1 print(expression) code2 %>
是 shorthand for<% code1 %><%= expression %><% code2 %>
.
车把:
{{name}}
在模板中插入 属性 的 HTML 转义字符串值,键为name
。{{{name}}}
做同样的事情,但是 没有 HTML-escaping.{{#if condition}}...{{/if}}
将仅在满足condition
时插入...
部分。{{#each name}}...{{/each}}
将为每个元素重复...
或name
的 属性。name
成为...
的 上下文 ;也就是说,如果您在...
中写入{{otherName}}
,Handlebars 将尝试将otherName
查找为由name
.[=268= 标识的对象的 属性 ]{{#name}}...{{/name}}
是 Handlebars 继承自 Mustache 的符号。当name
是一个数组时,它的行为类似于{{#each name}}
,否则类似于{{#if name}}
(如果它是一个对象,也会将上下文更改为name
)。这背后的想法(在 Mustache 中)是使模板更具声明性,或者像作者所说的那样“无逻辑”。- 还有更多的标签,我现在不会进入。
将 Underscore 模板转换为 Handlebars
由于 Underscore 允许在模板中插入任意 JavaScript 代码,因此并不总是可以将 Underscore 模板转换为等效的 Handlebars 模板。然而,幸运的是,模板并不真正需要 JavaScript 的完整表达能力,因此 Underscore 模板很可能以 可以 移植到更具限制性的方式编写模板语言(“偶然幸运”)。如果可能,以下策略在大多数情况下就足够了:
- 将出现在任何
<%...%>
标签内的任何print(_.escape(expression))
替换为%><%- expression %><%
。 - 将
<%...%>
中出现的任何其他print(expression)
替换为%><%= expression %><%
。 - 用
{{expression}}
. 替换所有出现的 - 用
{{{expression}}}
. 替换所有出现的 - 如果您在任何地方找到形式为
var name = otherName.propertyName
的别名(except 在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
。请注意,最后的<% } %>
保持不变 ;您要在它前面为每个中间else if
. 插入一个额外的 - 将最外面的
<% if (condition1) { %>...<% } %>
替换为{{#if condition1}}...{{/if}}
。这一次,最后的<% } %>
消失了。
{{/if}}
- 将最后的
- 替换循环,再次从最里面的表达式开始,然后从那里向外工作:
- 用符号
{{#each objectName}}...{{/each}}
替换 形式<% _.each(objectName, function(valueName, keyName, collectionName) { %>...<% }) %>
对象 的功能循环。检查...
中嵌套的{{}}
/{{{}}}
/{{#if}}
/{{#each}}
标签是否出现keyName
、collectionName
或objectName
并分别用@key
、..
和..
替换它们(这不是错字;collectionName
和objectName
都应替换为..
因为它们指的是同一个对象)。请注意,在 Underscore 版本中传递给_.each
的函数可能需要更少的参数,在这种情况下collectionName
甚至keyName
可能不存在;无论如何,替换工作都是一样的。 - 用符号
{{#each arrayName}}...{{/each}}
替换 形式<% _.each(arrayName, function(valueName, indexName, collectionName) { %>...<% }) %>
数组 上的函数循环。检查...
中嵌套的{{}}
/{{{}}}
/{{#if}}
/{{#each}}
标签是否出现indexName
、collectionName
或arrayName
并分别用@index
、..
和..
替换它们。同样,在 Underscore 版本中传递给_.each
的函数可能需要更少的参数;无论如何,替换工作都是一样的。 - 用符号
{{#each objectName}}...{{/each}}
替换 对象 形式<% for (var keyName in objectName) { %>...<% } %>
的程序循环。检查...
中嵌套的{{}}
/{{{}}}
/{{#if}}
/{{#each}}
标签是否出现keyName
、objectName[keyName]
或objectName
并分别替换为@key
、this
和..
。 - 用符号
{{#each arrayName}}...{{/each}}
替换 数组 形式<% for (var indexName = 0; indexName < arrayName.length; ++indexName) { %>...<% } %>
的程序循环。检查...
中嵌套的{{}}
/{{{}}}
/{{#if}}
/{{#each}}
标签是否出现indexName
、arrayName[indexName]
或arrayName
并分别替换为@index
、this
和..
。
- 用符号
- 修复表达式语法:
- 如果您在上一步中创建了
...propertyName
形式的表达式,其中前两个句点..
最初是一个名称(objectName
或arrayName
作为在步骤 7) 中有描述,用../propertyName
替换它。这种形式的路径可能更长,例如../../propertyName
. name[index1][index2]
形式的子表达式应转换为name.[index].[index2]
(注意句点)。
- 如果您在上一步中创建了
- 检查您翻译的所有
expression
和condition
是否可以被 Handlebars 按原样评估。根据经验,Handlebars 只能直接评估(嵌套)属性 当前上下文的名称(例如keyName
或keyName.subProperty
)以及它识别的一些特殊符号,例如 [= 90=、@index
、@root
、this
和..
。使用 helpers 来评估不仅仅是某些对象名称的表达式和@root
或..
所必需的锚点名称:- 请注意
this.propertyName
等同于propertyName
,因为this
指的是当前上下文。 - 当向模板传递像
{a: foo, b: {c: baz}}
这样的对象时,这个最外层对象的属性总是可以用@root.a
、@root.b.c
等引用。请注意,这个对象可能在原始 Underscore 模板中被赋予了自己的名称;在这种情况下,此名称本身可以替换为@root
. ..
用于在循环中引用父上下文,正如我们在步骤 7-8 中看到的那样。有时,原始 Underscore 模板中的循环可能会通过关闭直接引用父上下文的 属性 ;在这种情况下,您可以根据需要在 属性 的名称前加上../
来帮助 Handlebars 找到正确的 属性。
- 请注意
- 您可能在之前的转换后留下空的
<% %>
标签;这些可以安全地移除。
<%- expression %>
(包括在第 1 步之前已经存在的那些)
<%= expression %>
(包括在第 2 步之前已经存在的那些)
如果在执行上述步骤后,您的模板中仍有 <% code %>
符号,或 Handlebars 无法计算的表达式,您可能需要使用 Handlebars 语言的其他工具或创建特殊的解决方法。如果你很倒霉,模板根本无法翻译,但大多数时候会有办法的。
演示:您的模板
在此处重复您问题中的 Underscore 模板:
<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]
代替rates.length > 1
(因为Handlebars无法开箱即用地评估比较),我们成功获得了以下Handlebars模板:
<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>
您可能会发现其他需要翻译的模板。您可以对其他模板采用相同的方法。
最后说明:嵌入在 HTML
中的模板您的主题在页面中包含带有以下符号的模板。
<script id="shipping-calculator-response-template" type="text/template">
// the template
</script>
重要的是要认识到,虽然这是一个 <script>
标记,但浏览器实际上并不将内容解释为脚本。相反,因为标签有一个浏览器不知道的 type
,所以浏览器只是将标签留在 DOM 中并继续解释下一个元素。这是在 HTML 页面中嵌入任意文本数据的常用技巧,这样它就可以在以后由脚本获取,而无需用户看到它。在这种特殊情况下,你的 JavaScript 某处会做一些类似
var templateText = document.querySelector('#shipping-calculator-response-template').textContent;
然后将 templateText
传递给 Handlebars 以便对其进行处理。这也是为什么用 Underscore 替换 Handlebars 没有解决你的问题的原因;该脚本仍会尝试将模板传递给 Handlebars。最后,在您的特定情况下,可能没有必要放回 Underscore 参考。