Answers:
换句话说,它可以防止您用脚射击。在过去的JSP时代,JSP文件中充斥着Java代码是很常见的,由于您的代码分散,因此重构变得更加困难。
如果您通过设计来防止模板中的逻辑(如胡须那样),您将不得不将逻辑放在其他地方,因此模板最终将变得整洁。
另一个优点是,您被迫按照关注点分开进行思考:在将数据发送到UI之前,控制器或逻辑代码将必须进行数据批量处理。如果以后再将模板切换为另一个模板(假设您开始使用其他模板引擎),则转换将很容易,因为您只需要实现UI详细信息(因为模板上没有逻辑,请记住)。
我觉得自己几乎独自一人,但我坚决站在对面。我不认为在模板中可能混合使用业务逻辑是没有充分利用编程语言功能的充分理由。
无逻辑模板的通常说法是,如果您拥有对编程语言的完全访问权限,则可能会混入模板中没有位置的逻辑。我发现这类似于推理,您应该使用勺子切肉,因为如果您使用刀子,可能会割伤自己。这是真的,但是即使谨慎使用后者,您的工作效率也会大大提高。
例如,考虑使用髭的以下模板片段:
{{name}}:
<ul>
{{#items}}
<li>{{.}}</li>
{{/items}}
</ul>
我可以理解这一点,但是我发现以下内容(使用underscore)更加简单直接:
<%- name %>:
<ul>
<% _.each(items, function(i){ %>
<li><%- i %></li>
<% }); %>
</ul>
话虽如此,我确实了解无逻辑模板具有优势(例如,它们可以与多种编程语言一起使用而无需更改)。我认为这些其他优点非常重要。我只是不认为他们缺乏逻辑的本质就是其中之一。
name
并且items
可能包含JavaScript。
小胡子没逻辑吗?
这不是吗?
{{#x}}
foo
{{/x}}
{{^x}}
bar
{{/x}}
与此很相似吗?
if x
"foo"
else
"bar"
end
而且这与演示逻辑不是很相似吗?
if x > 0 && x < 10
)...因此,尽管可以在有或没有逻辑的情况下使用Mustache,但这取决于您。毕竟,这只是一个工具。
无逻辑模板是一种模板,其中包含供您填充而不是如何填充的孔。逻辑放置在其他位置,并直接映射到模板。这种关注点分离是理想的,因为这样可以轻松地使用不同的逻辑甚至使用不同的编程语言来构建模板。
从胡子手册:
我们称其为“无逻辑”,因为没有if语句,else子句或for循环。相反,只有标签。有些标签被替换为一个值,有些则被替换为空,而另一些则被替换为一系列值。本文档介绍了Mustache标签的不同类型。
不利的一面是,在拼命试图将业务逻辑排除在表示之外的情况下,最终将大量的表示逻辑放入模型中。一个常见的示例可能是您希望将“奇数”和“偶数”类放在表中的交替行上,这可以通过在视图模板中使用简单的模运算符来完成。但是,如果您的视图模板不允许您这样做,那么在模型数据中,您不仅必须存储哪一行是奇数或偶数,而且还取决于模板引擎的限制,您甚至可能需要污染模型带有实际CSS类的名称。视图应该与模型分开,并且是完全停止的。但是模型还应该与视图无关,这就是许多“无逻辑”模板引擎让您忘记的东西。逻辑在两个地方都有实际上确实能够正确地决定它的去向。是表示方面的关注,还是业务/数据方面的关注?为了获得100%原始的视图,污染只是降落在另一个不太可见但同样不合适的地方。
朝着另一个方向的运动正在增加,希望事情会集中在更合理的中间立场上的某个地方。
它使您的模板更整洁,并迫使您将逻辑保持在可以正确进行单元测试的位置。
这种对话的感觉就像是中年的僧侣们在争论一头大头针可以容纳多少天使时。换句话说,它开始感到宗教,徒劳和不正确地聚焦。
随之而来的小rant(随意忽略):
如果您不想继续阅读。我对上述主题的简短回答是:我不同意无逻辑模板。我认为这是极端主义的一种编程形式。:-) :-)
现在我的咆哮继续如火如荼::-)
我认为,当您将许多想法推向极致时,结果将变得可笑。有时(即本主题)问题是我们将“错误”的想法推向了极端。
从视图中删除所有逻辑是“荒谬的”和错误的想法。
后退片刻。
我们需要问自己的问题是为什么要取消逻辑?这个概念显然是关注点分离。使视图处理尽可能与业务逻辑分开。为什么这样 它使我们可以交换视图(针对不同的平台:移动,浏览器,桌面等),并且使我们可以更轻松地交换控制流,页面顺序,验证更改,模型更改,安全访问等。从视图(尤其是Web视图)中删除视图后,将使视图更具可读性,因此更易于维护。我理解并同意。
但是,首要的重点应该放在关注点分离上。不是100%无逻辑的视图。视图中的逻辑应与如何呈现“模型”有关。就我而言,视图中的逻辑非常好。您可以具有非业务逻辑的视图逻辑。
是的,当我们写JSP,PHP或ASP页面时,几乎没有或没有代码逻辑和视图逻辑的分离时,这些Web应用程序的维护绝对是一场噩梦。相信我,我创建并维护了其中的一些怪物。正是在那个维护阶段,我才真正(从表面上)理解了我和我同事的方式的错误。:-) :-)
因此,从高处(行业专家)发出的命令变成,您必须使用诸如前视图控制器(分派给处理程序或操作[挑选您的Web框架])之类的东西来构建Web应用程序,并且您的视图必须不包含任何代码。这些观点将成为愚蠢的模板。
因此,我大体上同意上述观点,不是因为法令条款的具体细节,而是因为法令背后的动机-这是希望在视图和业务逻辑之间分离关注点。
在我参与的一个项目中,我们尝试将无逻辑的观点想法推广到荒谬的极端。我们有一个自制的模板引擎,该引擎可让我们用html呈现模型对象。这是一个基于令牌的简单系统。这很可怕,原因很简单。有时,在我们必须决定的视图中,我是否应该显示此HTML的小片段。..该决定通常基于模型中的某些值。当视图中完全没有逻辑时,该怎么做?好吧,你不能。我和我们的架构师对此有一些主要争论。编写我们的视图的HTML前端人员在面对这一问题时完全受了阻碍,并且因为无法实现原本简单的目标而感到非常压力。因此,我在模板引擎中引入了简单IF语句的概念。我无法向您描述随之而来的解脱和平静。通过我们模板中的简单IF声明概念解决了问题!突然,我们的模板引擎变得不错了。
那么,我们如何陷入这种愚蠢的压力状态呢?我们专注于错误的目标。我们遵循规则,您的视图中不得包含任何逻辑。错了 我认为“经验法则”应尽量减少您视图中的逻辑量。因为如果不这样做,您可能会无意间使业务逻辑渗入视图中,这违反了关注点分离。
我了解到,当您声明“视图中必须没有逻辑”时,很容易知道您何时是“好”程序员。(如果这是您的善行程度)。现在,尝试使用上述规则实施具有中等复杂度的网络应用。它不是那么容易做到的。
对我来说,视图中的逻辑规则不是那么清晰,坦率地说,这就是我想要的。
当我在视图中看到很多逻辑时,我检测到代码气味,并尝试从视图中消除大多数逻辑-我试图确保业务逻辑位于其他地方-我试图将关注点分离。但是,当我开始与那些说必须从视图中删除所有逻辑的人聊天时,对我来说,这只是一点狂热,据我所知,您最终可能会遇到上述情况。
我的怒吼已完成。:-)
干杯,
大卫
对于无逻辑模板,我想出的最好论据是可以在客户端和服务器上使用完全相同的模板。但是,您并不是真的不需要逻辑,只有一个拥有自己的“语言”。我同意那些抱怨胡须无济于事的人。谢谢,但是我是个大男孩,在没有您帮助的情况下,我可以保持模板清洁。
另一个选择是找到一种模板语法,该模板使用客户端和服务器上都支持的语言,即使用node.js或服务器上的javascript,也可以通过诸如therubyracer之类的方法使用js解释器和json。
然后,您可以使用haml.js之类的东西,它比到目前为止提供的任何示例都干净得多,并且效果很好。
即使问题很老并且可以回答,我还是要加上2美分(听起来像是一声咆哮,但事实并非如此,这是关于限制以及何时变得无法接受)。
模板的目标是呈现某些内容,而不是执行业务逻辑。现在,在无法执行模板中需要执行的操作与在其中包含“业务逻辑”之间,存在一条微弱的界限。尽管我对Moustache确实很满意,并尝试使用它,但最终还是无法在非常简单的情况下完成所需的工作。
数据的“按摩”(使用接受的答案中的单词)可能会成为一个真正的问题-甚至不支持简单的路径(Handlebars.js解决的问题)。如果我有视图数据,并且每次由于模板引擎的限制而想要渲染某些东西时都需要进行调整,那么最终这将无济于事。而且,它克服了髭须自称的部分平台独立性;我必须在各处重复按摩逻辑。
就是说,经过一番挫折之后,在尝试了其他模板引擎之后,我们最终创建了自己的(...另一个……),它使用了受.NET Razor模板启发的语法。它在服务器上进行解析和编译,并生成一个简单的自包含JS函数(实际上是RequireJS模块),可以调用该函数“执行”模板,并返回一个字符串作为结果。当使用我们的引擎时,布拉德(Brad)给出的示例如下所示(我个人认为,与Mustache和Underscore相比,它在可读性上要优越得多):
@name:
<ul>
@for (items) {
<li>@.</li>
}
</ul>
在使用Mustache调用局部时,另一个无逻辑的限制对我们造成了打击。虽然Moustache支持局部函数,但无法自定义要首先传递的数据。因此,与其能够创建模块化模板并重用小块,不如说我将在模板中重复代码。
我们通过实现一种受XPath启发的查询语言(称为JPath)解决了这一问题。基本上,我们使用点而不是使用/遍历子对象,不仅支持字符串,数字和布尔文字,还支持对象和数组(就像JSON)。该语言是无副作用的(这对于模板化是必须的),但是允许通过创建新的文字对象按需“按摩”数据。
假设我们要渲染一个具有可定制标题的“数据网格”表,并链接到行上的操作,然后使用jQuery动态添加行。因此,如果我不想重复代码,则行必须是局部的。如果某些附加信息(例如应呈现哪些列)是视图模型的一部分,并且每行上的这些操作都是相同的,那么麻烦就从此开始。这是使用我们的模板和查询引擎的一些实际工作代码:
表格模板:
<table>
<thead>
<tr>
@for (columns) {
<th>@title</th>
}
@if (actions) {
<th>Actions</th>
}
</tr>
</thead>
<tbody>
@for (rows) {
@partial Row({ row: ., actions: $.actions, columns: $.columns })
}
</tbody>
</table>
行模板:
<tr id="@(row.id)">
@for (var $col in columns) {
<td>@row.*[name()=$col.property]</td>
}
@if (actions) {
<td>
@for (actions) {
<button class="btn @(id)" value="@(id)">@(name)...</button>
}
</td>
}
</tr>
从JS代码调用:
var html = table({
columns: [
{ title: "Username", property: "username" },
{ title: "E-Mail", property: "email" }
],
actions: [
{ id: "delete", name: "Delete" }
],
rows: GetAjaxRows()
})
它没有任何业务逻辑,但是它是可重用和可配置的,并且没有副作用。
row
对象获取的属性名称,而不是使用静态名称。例如,如果$col.property == 'Something'
这样将产生的内容row.Something
。
这是呈现列表的3种方式,包括字符数。除了第一个和最短的以外,其他所有语言都使用无逻辑模板语言。
CoffeeScript(带有反应式咖啡生成器DSL)-37个字符
"#{name}"
ul items.map (i) ->
li i
淘汰赛-100个字符
<span data-bind="value: name"/>
<ul data-bind="foreach: items">
<li data-bind="value: i"/>
</ul>
车把/小胡子-66个字符
{{name}}:
<ul>
{{#items}}
<li>{{.}}</li>
{{/items}}
</ul>
下划线-87个字符
<%- name %>:
<ul>
<% _.each(items, function(i){ %>
<li><%- i %></li>
<% }); %>
</ul>
我想,无逻辑模板的承诺是,具有更广泛技能的人们将能够管理无逻辑模板,而无需大惊小怪。但是,在以上示例中看到的是,当将最小逻辑语言添加到基于字符串的标记中时,结果会更复杂,而不是更少。另外,您看起来好像在使用老式的PHP。
显然,我不反对将“业务逻辑”(广泛的计算)放在模板之外。但是我认为,通过为他们提供一种显示逻辑的伪语言而不是一流的语言,价格是值得的。不仅要键入更多内容,而且还需要大量内容切换人员来阅读。
总而言之,我看不到无逻辑模板的逻辑,因此我认为它们的优势对我而言是零,但是我尊重社区中的许多人对此有不同的看法:)
我同意Brad的观点:这种underscore
风格更容易理解。但是我必须承认语法糖可能不会令所有人满意。如果_.each
有些令人困惑,则可以使用传统for
循环。
<% for(var i = 0; i < items.length; i++) { %>
<%= items[i] %>
<% } %>
如果您可以退回到标准结构(例如for
或),那总是很好if
。只需使用<% if() %>
或<% for() %>
同时Mustache
使用一些新词即可if-then-else
(如果您不阅读文档,就会感到困惑):
{{#x}}
foo
{{/x}}
{{^x}}
bar
{{/x}}
当您可以轻松实现嵌套模板(underscore
样式)时,模板引擎非常有用:
<script id="items-tmpl" type="text/template">
<ul>
<% for(var i = 0; i < obj.items.length; i++) { %>
<%= innerTmpl(obj.items[i]) %>
<% } %>
</ul>
</script>
<script id="item-tmpl" type="text/template">
<li>
<%= name %>
</li>
</script>
var tmplFn = function(outerTmpl, innerTmpl) {
return function(obj) {
return outerTmpl({obj: obj, innerTmpl: innerTmpl});
};
};
var tmpl = tmplFn($('#items-tmpl').html(), $('#item-tmpl').html());
var context = { items: [{name:'A',{name:'B'}}] };
tmpl(context);
基本上,您将内部tmpl传递为上下文的属性。并据此调用。甜:)
顺便说一句,如果您唯一感兴趣的是模板引擎,请使用独立模板实现。它是仅900个字符精缩(4个长行)时: