无逻辑模板(如胡须)的优点是什么?
最近,我遇到了自称为无逻辑模板的 小胡子 。
然而,没有解释为什么它被devise成无逻辑的方式。 换句话说,无逻辑模板的优点是什么?
换句话说,它可以防止你在脚下射击。 在过去的JSP时代,JSP文件中散布着Java代码是非常常见的,因为您的代码是分散的,这使得重构变得更加困难。
如果你通过devise来防止模板中的逻辑(就像小胡子一样),那么你将被迫把逻辑放在别的地方,这样你的模板就会变得简单。
另一个好处是,你不得不考虑分离问题:你的控制器或逻辑代码将不得不在数据发送到用户界面之前进行数据按摩 。 如果您以后将模板切换为另一个模板(假设您开始使用不同的模板引擎),则过渡将很容易,因为您只需实现UI细节(因为模板上没有逻辑,请记住)。
我觉得我几乎是孤身一人,但是我坚定地站在了相反的阵营。 我不相信你的模板中可能的业务逻辑混合是不足以使用你的编程语言的全部力量。
无逻辑模板的通常观点是,如果你有完全访问你的编程语言,你可能混合使用模板中没有位置的逻辑。 我觉得这种推理类似于你应该使用勺子来切肉,因为如果你用刀,你可以削减自己。 这是非常真实的,但是如果你使用后者,尽pipe仔细一点,你会更有成效。
例如,考虑使用胡子的以下模板片段:
{{name}}: <ul> {{#items}} <li>{{.}}</li> {{/items}} </ul>
我可以理解这一点,但我发现以下(使用下划线 )更简单直接:
<%- name %>: <ul> <% _.each(items, function(i){ %> <li><%- i %></li> <% }); %> </ul>
这就是说,我明白,无逻辑模板有优势(例如,它们可以与多种编程语言一起使用而不用更改)。 我认为这些其他优势非常重要。 我不认为他们的逻辑less性就是其中之一。
小胡子是没有逻辑的?
不是这样的:
{{#x}} foo {{/x}} {{^x}} bar {{/x}}
非常相似呢?
if x "foo" else "bar" end
这与expression逻辑的(几乎是一个定义)不是很相似吗?
无逻辑模板是一个模板,其中包含要填充的孔,而不是填充它们的方式。 逻辑放置在别处,并直接映射到模板。 这种关注的分离是理想的,因为这样模板可以很容易地用不同的逻辑,甚至用不同的编程语言来构build。
从胡子手册 :
我们称之为“无逻辑”,因为没有if语句,else子句或for循环。 相反,只有标签。 一些标签被replace为一个值,一些没有任何标签,而另外一些则是一系列的值。 本文档解释了不同types的Mustache标签。
它使得你的模板更加清晰,并且强制你将逻辑放在一个可以正确进行unit testing的地方。
硬币的另外一面是,为了避免业务逻辑摆脱陈述,最终在模型中放置了大量的表示逻辑。 一个常见的例子可能是你想在表中交替排列“奇数”和“偶数”类,这可以在视图模板中使用简单的模运算符来完成。 但是,如果你的视图模板不允许你这样做,那么在你的模型数据中,你不仅需要存储哪一行是奇数还是偶数,而是取决于你的模板引擎是多么的有限,你甚至可能需要污染你的模型与实际的CSS类的名称。 意见应与模型分开,全程。 但是模型也应该是View不可知的,这就是许多这些“无逻辑”的模板引擎让你忘记的东西。 逻辑在两个地方都有,但是你必须明白逻辑究竟是怎么做才能正确地决定它的去向。 这是一个关于演示的问题,还是业务/数据方面的问题? 为了达到100%的原始观点,污染只是另一个不太明显但同样不合适的地方。
在另一个方向上有一个不断增长的运动,希望事情会在更合理的中间地带的某个地方。
这个对话就好像中世纪的僧侣会争论有多less天使可以放在别针的末端一样。 换句话说,它开始感到宗教,徒劳和不正确的重点。
迷你随叫随到(随意忽略):
如果你不想继续阅读..我对上述主题的简短回应是:我不同意无逻辑模板。 我认为这是一种极端主义的编程forms。 🙂 🙂
现在我的咆哮继续如火如荼地进行::-)
我认为,当你将许多想法推向极端时,结果变得可笑。 有时(即这个话题)的问题是我们把“错误”的想法推到了极点。
从视图中去除所有的逻辑是“荒唐”和错误的想法。
退后一会儿。
我们需要问自己的问题是为什么要删除逻辑? 这个概念显然是关注点的分离 。 尽可能保持视图处理与业务逻辑分离。 为什么这样做? 它允许我们交换视图(对于不同的平台:移动,浏览器,桌面等),它允许我们更容易地换出控制stream,页面顺序,validation更改,模型更改,安全访问等。另外,当逻辑从视图(特别是networking视图)中删除,它使视图更具可读性,因此更易于维护。 我明白并同意这一点。
然而,首要的重点应放在关注点的分离上。 不是100%无逻辑的意见。 视图内的逻辑应与如何渲染“模型”有关。 就我而言,意见中的逻辑是完全正确的。 您可以拥有非业务逻辑的视图逻辑。
是的,当我们编写JSP,PHP或ASP页面的时候,几乎没有代码逻辑和查看逻辑的分离,维护这些Web应用程序是绝对的噩梦。 相信我,我知道,我创造,然后保持这些怪物的一些。 正是在这个维护阶段,我才真正理解了我和我同事的错误。 🙂 🙂
因此,来自高层(行业专家)的诫命变成了,你必须使用类似前视控制器(派到处理程序或行动(select你的networking框架))来构build你的networking应用程序,并且你的视图必须不包含代码。 意见是成为愚蠢的模板。
因此,我总体上同意上述观点,而不是诏书的具体内容,而是诏书背后的动机 – 这是对观点与商业逻辑之间的分离关注的愿望。
在我参与的一个项目中,我们试图将无逻辑的观点理念追随到荒谬的极端。 我们有一个本土的模板引擎,可以让我们在html中渲染模型对象。 这是一个简单的基于令牌的系统。 一个非常简单的原因是可怕的。 有时我们不得不决定是否应该显示这个HTML代码片断。这个决定通常是基于模型中的某个值。 当你完全没有逻辑的时候,你怎么做? 那么你不能。 我和我们的build筑师有一些主要的争论。 我们的观点的前端HTML人在面对这个问题时完全受到了束缚,并且因为无法实现他们简单的目标而感到非常紧张。 所以我在模板引擎中引入了一个简单的IF语句的概念。 我无法向你描述随之而来的安慰和冷静。 问题是在我们的模板中用一个简单的IF-Statement概念解决的! 突然之间,我们的模板引擎变好了。
那么我们是如何进入这种无聊的压力的呢? 我们专注于错误的目标。 我们遵循这个规则,在你的观点中你不应该有任何逻辑。 那是错的。 我认为,“经验法则”应该是,尽量减less你的观点的逻辑。 因为如果你不这样做,你可能会无意中让业务逻辑悄然进入视图 – 这违反了关注点的分离。
我明白,当你声明“你在视图中一定没有逻辑”时,就很容易知道你什么时候是一个“好”的程序员。 (如果这是你的善举)。 现在尝试使用上述规则来实现一个甚至中等复杂度的Web应用程序。 它不是那么容易做到的。
对于我来说,意见中的逻辑规则并不是那么清楚,坦率地说,这就是我所希望的。
当我在视图中看到很多逻辑时,我会检测到代码异味,并尝试从视图中消除大部分逻辑 – 我试图确保业务逻辑能够在别处生存 – 我试图区分这些问题。 但是当我开始和那些说我们必须从视图中删除所有逻辑的人聊天的时候,那么对我来说,那只是狂热的狂热,因为我知道你可能会像我上面所描述的那样。
我完成了我的咆哮。 🙂
干杯,
大卫
我已经提出了无逻辑模板的最佳理由,那么您可以在客户端和服务器上使用完全相同的模板。 但是,你并不需要无逻辑的,只有一个拥有自己的“语言”的逻辑。 我同意那些抱怨小胡子毫无意义的限制。 谢谢,但我是一个大男孩,我可以保持我的模板干净没有你的帮助。
另一个select是find一个使用客户端和服务器都支持的语言的模板语法,即通过使用node.js在服务器上的javascript,或者您可以通过类似therubyracer的东西来使用js解释器和json。
那么你可以使用haml.js这样的东西,比迄今为止提供的任何示例都要干净得多,而且效果很好。
在一句话中:无逻辑意味着模板引擎本身不那么复杂,因此具有较小的占用空间,并且出现意外行为的方式较less。
即使这个问题已经老了,我还想补充2分(这听起来像是咆哮,但不是,这是关于限制,什么时候变得不可接受)。
模板的目标是渲染一些东西,而不是执行业务逻辑。 现在,在一个模板中不能做你需要做的事情,并在其中有“业务逻辑”之间有一条细线。 尽pipe我对胡须非常乐观,并试图使用它,但最终却无法在非常简单的情况下做我所需要的。
数据的“按摩”(使用被接受的答案中的单词)可能成为一个真正的问题 – 甚至不支持简单的path(Handlebars.js地址)。 如果我有查看数据,并且需要调整每次我想渲染的东西,因为我的模板引擎太有限制,那么这对最终没有帮助。 它打败胡须自称的平台独立的一部分; 我必须到处复制按摩逻辑。
也就是说,在经历了一些挫折和尝试其他模板引擎之后,我们最终创build了自己的(…又一个…),它使用了受.NET Razor模板启发的语法。 它在服务器上被parsing和编译,并生成一个简单的,自包含的JS函数(实际上就是RequireJS模块),可以调用它来“执行”模板,返回一个string作为结果。 Brad给出的样本在使用我们的引擎时看起来就像这样(我个人发现它比Mustache和Underscore要好得多):
@name: <ul> @for (items) { <li>@.</li> } </ul>
另一个无逻辑的限制在用胡子调用部分函数时打了我们。 尽pipeMustache支持部分,但是不可能先定制要传入的数据。 所以,不能创build一个模块化的模板和重新使用小块,我将最终做模板与重复的代码。
我们通过实现受XPath启发的查询语言(我们称之为JPath)来解决这个问题。 基本上,我们不使用string,数字和布尔文字,而是使用对象和数组(像JSON),而不是使用/来遍历子元素。 语言是无副作用的(这是模板的必需),但允许通过创build新的文字对象按需要“按摩”数据。
比方说,我们想渲染一个“数据网格”的表格,其中包含可自定义的头部和指向行的动作链接,之后使用jQuerydynamic添加行。 因此,如果我不想复制代码,那么这些行就需要处于不完整的状态。 如果一些附加信息(比如应该呈现哪些列)是视图模型的一部分,那么就是麻烦开始的地方,而对于每一行的这些动作也是如此。 以下是使用我们的模板和查询引擎的一些实际工作代码:
表模板:
<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() })
它没有任何业务逻辑,但它是可重用和可configuration的,也是无副作用的。
这里有三种呈现列表的方式,包括字符数。 除了第一个和最短的一个都是无逻辑的模板语言。
CoffeeScript(与反应咖啡build设者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>
我认为,无逻辑模板的承诺是,具有更广泛技能的人将能够pipe理无逻辑的模板,而不会在自己的脚下自我牺牲。 然而,你在上面的例子中看到的是,当你将最小逻辑语言添加到基于string的标记时,结果会更复杂,而不是更less。 另外,你看起来像你在做老派的PHP。
显然,我不反对保持“业务逻辑”(广泛的计算)模板。 但我认为,通过给他们显示逻辑而不是一stream的语言的伪语言,价格是支付的。 不仅仅是更多的types,而是一个令人讨厌的上下文切换混合的人需要阅读它。
总之,我没有看到逻辑模板的逻辑,所以我会说他们的优势是零,但我尊重社区中的许多人看到它不同:)
我同意布拉德: 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个长线):
使用无逻辑模板的主要优点是:
- 严格执行模型视图分离 ,详见本文 (强烈推荐)
- 非常容易理解和使用 ,因为不需要编程逻辑(和知识!),语法也很less
- (尤其是小胡子)的高度可移植性和语言独立性,可以在几乎所有的编程环境中使用,而无需修改