无逻辑模板(例如胡须)的优点是什么?


100

最近,我遇到了一个据称是无逻辑模板的胡须

但是,没有解释为什么以无逻辑方式进行设计。换句话说,无逻辑模板的优点是什么?

Answers:


107

换句话说,它可以防止您用脚射击。在过去的JSP时代,JSP文件中充斥着Java代码是很常见的,由于您的代码分散,因此重构变得更加困难。

如果您通过设计来防止模板中逻辑(如胡须那样),您将不得不将逻辑放在其他地方,因此模板最终将变得整洁。

另一个优点是,您被迫按照关注点分开进行思考:在将数据发送到UI之前,控制器或逻辑代码将必须进行数据批量处理。如果以后再将模板切换为另一个模板(假设您开始使用其他模板引擎),则转换将很容易,因为您只需要实现UI详细信息(因为模板上没有逻辑,请记住)。


30
那么,为什么控制器代码必须以一种特定的方式呈现数据所需要的形式来对数据进行按摩是一件好事呢?为什么更改演示文稿经常需要更改控制器代码是一件好事?对我来说,这似乎是一件坏事。我认为模板语言的一个目标是将控制器逻辑与表示逻辑分离。无法做出任何决定的哑模板将表示逻辑重新输入到控制器代码中。在由其他人进行演示的组织中,他们不能自己完成工作。
jfriend00 2014年

8
您不应在控制器中为视图准备数据。控制器用于控制应用程序流。相反,您应该做的是创建一个presenter类,该类将您的模型转换为视图模型,然后由视图使用。视图模型是用于UI的模型,它与作为业务逻辑一部分的模型是分开的。我建议您观看罗伯特·“鲍勃叔叔”·马丁的“清洁建筑与设计”演讲:youtu.be/asLUTiJJqdE
ragol 2014年

1
它强制一些表示逻辑进入控制器,但这有助于使各个表示层相互重叠。如果您希望类似的数据以JSON,HTML或XML的形式呈现,则不应在HTML模板中将数据扭曲100%。这将使JSON和XML具有自己的扭曲显示逻辑。您正在将80%的显示逻辑集中在控制器中,并将其他20%嵌入lambda和过滤器中。
chugadie 2016年

65

我觉得自己几乎独自一人,但我坚决站在对面。我不认为在模板中可能混合使用业务逻辑是没有充分利用编程语言功能的充分理由。

无逻辑模板的通常说法是,如果您拥有对编程语言的完全访问权限,则可能会混入模板中没有位置的逻辑。我发现这类似于推理,您应该使用勺子切肉,因为如果您使用刀子,可能会割伤自己。这是真的,但是即使谨慎使用后者,您的工作效率也会大大提高。

例如,考虑使用的以下模板片段:

{{name}}:
<ul>
  {{#items}}
    <li>{{.}}</li>
  {{/items}}
</ul>

我可以理解这一点,但是我发现以下内容(使用underscore)更加简单直接:

<%- name %>:
<ul>
<% _.each(items, function(i){ %>
  <li><%- i %></li>
<% }); %>
</ul>

话虽如此,我确实了解无逻辑模板具有优势(例如,它们可以与多种编程语言一起使用而无需更改)。我认为这些其他优点非常重要。我只是不认为他们缺乏逻辑的本质就是其中之一。


135
我感到惊讶的是,您感到下划线模板更加简单。只是一个意见:-)
本·克莱顿

9
@ ben-clayton我同意第一种语法更漂亮,也许更具可读性。但是,这是我说“简单”时要考虑的复杂性。
布拉德

1
有时,无论您有什么逻辑,在模板中使用三元语句都是合适的。或者,如果我想输出数组中的项目数怎么办?我不知道该如何使用胡须,它似乎缺少这种东西。
保罗·夏皮罗

5
@PaulShapiro您将在模板之外的数组中生成项目数,并将其存储在单独的变量中。
Seen Osewa

4
@brad,下划线模板引入了XSS漏洞:name并且items可能包含JavaScript。
马修,

28

小胡子没逻辑吗?

这不是吗?

{{#x}}
  foo
{{/x}}
{{^x}}
  bar
{{/x}}

与此很相似吗?

if x
  "foo"
else
  "bar"
end

而且与演示逻辑不是很相似吗?


17
是的,如果您只是评估一个对象的真实性,但是尝试做任何更复杂的事情是不可能的(if x > 0 && x < 10)...因此,尽管可以在有或没有逻辑的情况下使用Mustache,但这取决于您。毕竟,这只是一个工具。
汤姆·阿什沃思

5
您正在以错误的方式看待这个问题。仅仅因为您可以用某种语言来做一些与它的一般范式不完全相同的事情,并不意味着该语言也不属于该范式。您可以用Java或Haskell编写命令式代码,但是您不会说这使Java既不是OO语言,也不是Haskell不是功能语言。查看哪种语言可以使您轻松完成工作,以及如何引导您查看它是哪种语言。
cjs

@ CurtJ.Sampson如果Java将自己出售为“缺乏功能”或Haskell将其出售为“缺乏面向对象”,那么您的类比是有意义的。这个答案表明“缺乏逻辑”充其量是一个误称。
Corey

1
我认为您同意我的观点,当我们将语言称为“功能性”时,是指“趋于功能化”。似乎不同之处在于,我将“无X”定义为“趋于X”,而将其定义为“完全无法做X”。
cjs

13

无逻辑模板是一种模板,其中包含供您填充而不是如何填充的孔。逻辑放置在其他位置,并直接映射到模板。这种关注点分离是理想的,因为这样可以轻松地使用不同的逻辑甚至使用不同的编程语言来构建模板。

胡子手册

我们称其为“无逻辑”,因为没有if语句,else子句或for循环。相反,只有标签。有些标签被替换为一个值,有些则被替换为空,而另一些则被替换为一系列值。本文档介绍了Mustache标签的不同类型。


6
该描述有些复杂,因为这些部分的工作方式非常像条件和循环,只是条件和循环非常有限。在散列中引用可调用对象的能力还使您可以将节转换为基本的编程语言。尽管如此,至少这使它很难走那条路,而不是鼓励它。
汤姆·安德森

1
当然,但是没有条件和迭代块的模板系统将相对无用。模板本身未指定块的用途或处理方式。
杰里米

12

不利的一面是,在拼命试图将业务逻辑排除在表示之外的情况下,最终将大量的表示逻辑放入模型中。一个常见的示例可能是您希望将“奇数”和“偶数”类放在表中的交替行上,这可以通过在视图模板中使用简单的模运算符来完成。但是,如果您的视图模板不允许您这样做,那么在模型数据中,您不仅必须存储哪一行是奇数或偶数,而且还取决于模板引擎的限制,您甚至可能需要污染模型带有实际CSS类的名称。视图应该与模型分开,并且是完全停止的。但是模型还应该与视图无关,这就是许多“无逻辑”模板引擎让您忘记的东西。逻辑在两个地方都有实际上确实能够正确地决定它的去向。是表示方面的关注,还是业务/数据方面的关注?为了获得100%原始的视图,污染只是降落在另一个不太可见但同样不合适的地方。

朝着另一个方向的运动正在增加,希望事情会集中在更合理的中间立场上的某个地方。


6
在那里与您不同意。无逻辑模板的表示逻辑不会出现在模型中,而是出现在它所属的视图中。该视图获取原始模型数据,并根据需要进行按摩(通过注释奇数/偶数行等)以准备进行演示。
2012年

1
为什么视图不能超过模板,而不能包含用于按摩模板数据的代码?
LeeGee 2013年

1
acjohnson55-我同意您应该去哪里(视图),但是没有逻辑的模板可以防止很多情况。@LeeGee-我认为对视图中过多的逻辑存在强烈的反感,但事情在另一个方向上走得太远,以至于甚至无法阻止视图中特定于演示的逻辑。
mattmc3

4
我知道我迟到了,但是CSS nth-item(odd)应该用于交替显示行颜色。
DanRedux 2014年

1
Threre通过视图从模型和集合或任何其他数据创建中间表示对象没有错。这样,视图就可以创建演示数据,并与模板合并以呈现视图。
wprl 2014年

11

它使您的模板更整洁,并迫使您将逻辑保持在可以正确进行单元测试的位置。


2
您可以详细解释吗?
Morgan Cheng

29
花了三个月的时间在一个使用逻辑模板语言(例如JSP)的系统上工作,程序员不热衷于分离逻辑和表示。您会发现您构建了一个基本上在页面上进行编程的系统-表格布局的算术运算,显示哪些定价信息的条件等等。模板语言是极其差劲的编程语言,因此这成为开发和维护的噩梦。首先,像小胡子之类的东西不会让您陷入那种情况。
汤姆·安德森

如果逻辑采用函数形式,则可由支持嵌入式逻辑经过单元测试的微模板系统使用。在我看来,最糟糕的主意是把手“助手”(请参见spin.atomicobject.com/2011/07/14/… “高级用法”)-由于这些脚本位于脚本/中,因此似乎无法进行单元测试类型/文字标签,而不是普通的旧脚本(/ type / javascript)标签
Dexygen

8

这种对话的感觉就像是中年的僧侣们在争论一头大头针可以容纳多少天使时。换句话说,它开始感到宗教,徒劳和不正确地聚焦。

随之而来的小rant(随意忽略):

如果您不想继续阅读。我对上述主题的简短回答是:我不同意无逻辑模板。我认为这是极端主义的一种编程形式。:-) :-)

现在我的咆哮继续如火如荼::-)

我认为,当您将许多想法推向极致时,结果将变得可笑。有时(即本主题)问题是我们将“错误”的想法推向了极端。

从视图中删除所有逻辑是“荒谬的”和错误的想法。

后退片刻。

我们需要问自己的问题是为什么要取消逻辑?这个概念显然是关注点分离。使视图处理尽​​可能与业务逻辑分开。为什么这样 它使我们可以交换视图(针对不同的平台:移动,浏览器,桌面等),并且使我们可以更轻松地交换控制流,页面顺序,验证更改,模型更改,安全访问等。从视图(尤其是Web视图)中删除视图后,将使视图更具可读性,因此更易于维护。我理解并同意。

但是,首要的重点应该放在关注点分离上。不是100%无逻辑的视图。视图中的逻辑应与如何呈现“模型”有关。就我而言,视图中的逻辑非常好。您可以具有非业务逻辑的视图逻辑。

是的,当我们写JSP,PHP或ASP页面时,几乎没有或没有代码逻辑和视图逻辑的分离时,这些Web应用程序的维护绝对是一场噩梦。相信我,我创建并维护了其中的一些怪物。正是在那个维护阶段,我才真正(从表面上)理解了我和我同事的方式的错误。:-) :-)

因此,从高处(行业专家)发出的命令变成,您必须使用诸如前视图控制器(分派给处理程序或操作[挑选您的Web框架])之类的东西来构建Web应用程序,并且您的视图必须不包含任何代码。这些观点将成为愚蠢的模板。

因此,我大体上同意上述观点,不是因为法令条款的具体细节,而是因为法令背后的动机-这是希望在视图和业务逻辑之间分离关注点。

在我参与的一个项目中,我们尝试将无逻辑的观点想法推广到荒谬的极端。我们有一个自制的模板引擎,该引擎可让我们用html呈现模型对象。这是一个基于令牌的简单系统。这很可怕,原因很简单。有时,在我们必须决定的视图中,我是否应该显示此HTML的小片段。..该决定通常基于模型中的某些值。当视图中完全没有逻辑时,该怎么做?好吧,你不能。我和我们的架构师对此有一些主要争论。编写我们的视图的HTML前端人员在面对这一问题时完全受了阻碍,并且因为无法实现原本简单的目标而感到非常压力。因此,我在模板引擎中引入了简单IF语句的概念。我无法向您描述随之而来的解脱和平静。通过我们模板中的简单IF声明概念解决了问题!突然,我们的模板引擎变得不错了。

那么,我们如何陷入这种愚蠢的压力状态呢?我们专注于错误的目标。我们遵循规则,您的视图中不得包含任何逻辑。错了 我认为“经验法则”应尽量减少您视图中的逻辑量。因为如果不这样做,您可能会无意间使业务逻辑渗入视图中,这违反了关注点分离。

我了解到,当您声明“视图中必须没有逻辑”时,很容易知道您何时是“好”程序员。(如果这是您的善行程度)。现在,尝试使用上述规则实施具有中等复杂度的网络应用。它不是那么容易做到的。

对我来说,视图中的逻辑规则不是那么清晰,坦率地说,这就是我想要的。

当我在视图中看到很多逻辑时,我检测到代码气味,并尝试从视图中消除大多数逻辑-我试图确保业务逻辑位于其他地方-我试图将关注点分离。但是,当我开始与那些说必须从视图中删除所有逻辑的人聊天时,对我来说,这只是一点狂热,据我所知,您最终可能会遇到上述情况。

我的怒吼已完成。:-)

干杯,

大卫


伟大的答案,它是所有关于良好的编程习惯..顺便说一句,在服务器和客户端业务逻辑的重用少的模板是不会干,因为你将不得不重复的逻辑在两个..
mateusmaso

5

对于无逻辑模板,我想出的最好论据是可以在客户端和服务器上使用完全相同的模板。但是,您并不是真的不需要逻辑,只有一个拥有自己的“语言”。我同意那些抱怨胡须无济于事的人。谢谢,但是我是个大男孩,在没有您帮助的情况下,我可以保持模板清洁。

另一个选择是找到一种模板语法,该模板使用客户端和服务器上都支持的语言,即使用node.js或服务器上的javascript,也可以通过诸如therubyracer之类的方法使用js解释器和json。

然后,您可以使用haml.js之类的东西,它比到目前为止提供的任何示例都干净得多,并且效果很好。


我完全同意。经过一番阅读之后,我得出的结论是,在客户端和服务器端使用JS进行模板化的能力比一堆特定的模板语言更好地满足了我的需求
christofr

4

一句话:少逻辑意味着模板引擎本身不那么复杂,因此具有较小的占用空间,并且有更少的方式使其行为异常。


3

即使问题很老并且可以回答,我还是要加上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()
})

它没有任何业务逻辑,但是它是可重用和可配置的,并且没有副作用。


13
老实说,我看不到这个答案的意义,根本没有回答这个问题。您说的是限制,而没有实际描述它们是什么或它们如何发生。最后,您开始讨论自己的系统,该系统对其他任何人都不可用,实际上,它可能很糟糕,并且可能有一天会在thedailywtf上结束,但我们永远不会知道。开源,链接到它,让我们决定!
mattmanser '02

1
@mattmanser,我确实写了一些导致不使用Mustache(和Handlebars)的局限性:缺少对路径,谓词,变量,调用局部函数的数据转换的支持。这些对我们都很重要。我们可能有一天会开源代码,但是由于它是一个服务器端(或编译时,如果需要的话)解决方案,可以将模板编译为JS代码,因此受众群体不会像Mustache那样大。如果您想取得联系,请随时与我联系-我也是bsn GoldParser在Google代码上的作者(您可以在自述文件中轻松找到我的电子邮件)。
Lucero '02

我喜欢您的剃须刀像引擎一样的想法...它是开源的吗?
饼干2012年

@Cracker,不,不是(还)-问题是当前模板已在运行ASP.NET MVC的服务器上编译为JS代码,因此目标受众并不像其他模板引擎那么大。除此之外,它是一个更大的库的一部分,我们首先必须拆开该库以开源。
Lucero 2012年

1
@Esailija它允许动态指定要从row对象获取的属性名称,而不是使用静态名称。例如,如果$col.property == 'Something'这样将产生的内容row.Something
Lucero 2013年

2

这是呈现列表的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,您如何看待这种比较?
Dean Radcliffe 2014年

2

使用无逻辑模板的主要优点是:

  • 严格执行模型与视图的分离,有关详细信息,请参阅本文(强烈建议)
  • 非常容易理解和使用,因为不需要编程逻辑(和知识!),语法也很少
  • (特别是Mustache)高度可移植且独立于语言,几乎可以在几乎所有编程环境中使用而无需修改

简明扼要的答案!
MrWatson

1

我同意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个长行)时:

https://gist.github.com/marlun78/2701678

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.