【问题标题】:How can I avoid tag soup with MVC?如何避免使用 MVC 标记汤?
【发布时间】:2012-01-29 11:43:15
【问题描述】:

我正在阅读这篇文章,以证明 MVC 优于常规旧 php 的非 mvc(不使用 MVC,即使是经典的 asp 也可以使用,尽管很痛苦):

http://www.codinghorror.com/blog/2008/07/web-development-as-tag-soup.html

我找不到答案。我认为得到标签汤是不可避免的。是的,我知道 MVC 将模型和控制器分开,但是当你看到视图时,一切都变得可怕了。我可以阅读发出的 html,与标记汤一样好或更好。

我不会使用单元测试,所以这并不是一个重要的优势。我不确定如何避免丑陋的视图,现在无论我如何让它枯萎 mvc 或只是发出 html。

我不认为使用所有奇怪的编码(它是代码)来维护视图比使用 response.write "<table>" 更容易。

示例:Dealing with ASP.NET MVC "tag soup"

Arnis 的回答(对他或其他任何人都没有冒犯)修复了问题中的可怕代码,但对我来说这仍然看起来很糟糕,或者至少不是我所期望的。对我来说,那些尖括号也可能是<% %><?php ?>

我喜欢 codeigniter 之类的东西,它确实是我见过的最干净的东西,但它仍然不是我所期望的。我想我希望 MVC 中存在一些让一切变得美丽的魔法。显然,除非真的很小心,否则没有比使用经典 asp 更好的方法了,因为它与视图有关。

这主要是关于视图。不是关于哪种语言更适合什么或谁的模板引擎最棒(它们都有相同的标记混合倾向)。

相信我。我希望让 MVC 与我的共同开发人员一起工作,所以我根本不反对它作为一种范式。我不能让他们仅仅因为每个人都在这样做或类似的事情就同意某件事。

感谢任何 cmets。我必须能够证明这些事情的合理性,虽然我了解 MVC 以及我得到的东西,但这种观点让很多事情看起来像是在浪费时间。

edit:一切似乎都针对特定框架而不是计划。我看到了一些见解,但最终似乎除了纪律之外别无他法。谢谢大家的回答。

【问题讨论】:

  • 对您的编辑的评论 johnny - 您是否意识到 Razor 视图引擎包含在 MVC 3 中?不需要额外的框架,所以不需要说服你的老板购买或下载任何额外的东西!
  • @connell 我的问题不仅仅是关于 asp.net 和它拥有的任何脚本引擎。它包含任何 MVC 设置。 asp.net 只是一个例子。
  • 啊,那我很抱歉。我误解了,但 Marcin 似乎也误解了,他添加了asp.net-mvc 标签;D
  • @ConnellWatkins 我认为最容易被误解。它们都是很好的答案,但与 asp.net 的联系如此紧密,以至于它并没有像我希望的那样有帮助。我认为最终的答案是,只要遵守纪律,以及一些不该做什么的指示。不过,我确实喜欢 Razor 解决方案。

标签: asp.net-mvc model-view-controller


【解决方案1】:

看看使用 Razor 视图引擎,它包含在 MVC 3 中。还要尝试将所有逻辑保留在 Controller 类中,并根据 View 中显示的内容构建 Model

  • Razor 是避免标签汤的一种明显方法,因为不需要任何 <%%> 标签 - 只需在您的代码之前添加一个 @,视图引擎就会确定C# 结束,HTML 开始。

    <span class="name">@Model.Name</span>
    

    在带有 Razor 和神奇的 @ 字符的 .cshtml 文件中,即使循环和 if 语句仍然看起来很性感。

    @if(shouldDisplayDiv) {
        <div id="mydiv">Div is displayed!</div>
    }
    
    
    @foreach(User user in Model.Friends) {
        <a href="@user.Url"><img src="@user.ImageUrl" title="@user.Name" /></a>
    }
    

    默认情况下,Razor 还会为您处理 HTML 编码,因此您的视图不会充满 Html.Encode 调用。 (注意:如果需要输出HTML,可以使用Html.Raw辅助方法)。

  • 将您的逻辑放入控制器将理想地消除视图中对大型代码块的需求。尝试让模型对象包含视图的所有动态数据,与视图中显示的完全一样。力争在您的视图中完全没有任何 C# 代码(毫无意义,但如果这是目标,看看您能离它多近!)。

  • 局部视图可以很好地分隔视图的不同部分(但尽量不要过多使用它们)。您还可以将不同的模型对象传递给每个局部视图,我发现这对于一些大型循环或 flair 之类的东西很方便。

  • HTML Helpers 也非常有用(感谢 subkamran)。这里有一个与上面提到的局部视图类似的概念,但 HtmlHelpers 略有不同,因为您指定方法的参数(及其类型),而不是您可以传递单个模型对象的局部视图。这是good example of how to implement them。同样,这些在您的 cshtml 代码中看起来非常整洁。

     <div class="specialdiv">@Html.SomeMethod(Model, "String", 5)</div>
    
  • 客户端 MVC 是另一种选择,如果您正在开发重 AJAX 的 Web 应用程序,这是一个强烈的建议。遵循 控制器中的逻辑,您将使用客户端 MVC 框架(如 Backbone.js)以整洁的方式模板化 HTML,并使用 jQuery .ajax() 与控制器来回交谈。这是分离表示层的好习惯,为您留下一些漂亮的视图标记!

我坚持这些小准则,它对我来说就像一种魅力。漂亮、干净的 HTML 标记,偶尔带有 @ 字符。非常容易维护(至少视图是!)。

编辑:请注意,所有这些点都包含在 ASP.NET MVC 3 中,并且就 Microsoft 而言都是“最佳实践”。无需安装任何额外的框架、插件或插件即可遵守这些准则。

【讨论】:

  • 同意;剃须刀让我全身发麻。我看过一些复杂的 Razor 视图,它们仍然很好看,这取决于它们的效果如何。除了 Razor Helpers(例如 App_Code\Helpers.cshtml@helper 语法)之外,部分视图让事情变得更好。
  • 帮手!谢谢,我已将这些添加到答案中。
  • 感谢@one.beat.consumer 添加客户端项目符号。我打算将它放入(或类似的)中,但不确定它是否真的有助于解决 tag soup 问题。您解释它的方式几乎可以解决!我已重新编辑,因为您的格式在其中两个部分的缩进错误:P 谢谢!
【解决方案2】:

MVC 越来越受欢迎是有原因的。虽然确实有一些标签添加到视图中,但如果逻辑在它应该属于的控制器中正确处理,则它会更清晰。

了解 MVC 的真正含义也很重要:http://en.wikipedia.org/wiki/Model-view-controller。您从中获得的优势是更清洁的分离和易于替换。

考虑一下。您有一个客户希望您编写一个同时支持传统浏览器和移动浏览器的应用程序。使用 MVC 模式,让控制器检测平台并更改呈现的视图非常容易。如果操作正确,将一个视图换成另一个视图应该是一个非常简单的过程。

我有 7 年的 ASP.NET Forms 应用程序编写经验。一旦我切换到 MVC 并开始了解 MVC,我意识到我永远不会回去。视图更干净,调试更简单,逻辑更明显。我写的最后一个应用程序使用 MVC 和 jQuery,每天有 3000 名用户,已经成为我们现在所有网站都为其编写的模型网站。

我们的客户要求我们为我们的网站添加移动支持。因为我们在实现中选择了 MVC,所以我们用了 1 周的时间来添加对移动设备的全面支持。如果我们在 ASP.NET Forms 中完成它,我们不可能如此快速地完成它并如此有效地利用代码。

虽然来自http://www.codinghorror.com/blog/2008/07/web-development-as-tag-soup.html 的示例代码看起来很糟糕,但您是否看过 ASP.NET GridView? HTML是残酷的。您提供的示例还显示了可以做一些工作来清理他们的视图的人。这是网格视图与 MVC 与 Razor 的比较:

网格视图:

<asp:datagrid id="voucherGrid" runat="server" CssClass="dg" CellPadding="2" AutoGenerateColumns="False" DataKeyField="cx_nbr" 
        Width="800px" AllowPaging="True" AllowSorting="True" PageSize="20" OnPageIndexChanged="voucherGrid_PageIndexChanged" 
        OnSortCommand="voucherGrid_SortCommand" OnItemDataBound="voucherGrid_ItemDataBound">
        <SelectedItemStyle CssClass="dgSelectItem"></SelectedItemStyle>
        <AlternatingItemStyle CssClass="dgAlternateItem"></AlternatingItemStyle>
        <ItemStyle CssClass="dgNormalItem"></ItemStyle>
        <HeaderStyle ForeColor="White" CssClass="dgHeader"></HeaderStyle>

        <Columns>
            <asp:TemplateColumn HeaderText="Image">
                <ItemStyle HorizontalAlign="Center"></ItemStyle>
                <ItemTemplate>
                    <asp:HyperLink id="voucherImageLink" Target="_blank" runat="server">Image</asp:HyperLink>                                               
                </ItemTemplate>
            </asp:TemplateColumn>
            <asp:BoundColumn DataField="cx_voucher_nbr" SortExpression="cx_voucher_nbr" HeaderText="Call #">
                <ItemStyle HorizontalAlign="Center"></ItemStyle>
            </asp:BoundColumn>
            <asp:BoundColumn DataField="cx_pkup_datetime" SortExpression="cx_pkup_datetime" HeaderText="Date" DataFormatString="{0:MM/dd/yyyy}">
                <ItemStyle HorizontalAlign="Center"></ItemStyle>
            </asp:BoundColumn>
            <asp:BoundColumn DataField="cx_pass_name" SortExpression="cx_pass_name" HeaderText="Passenger">
                <ItemStyle Wrap="False"></ItemStyle>
            </asp:BoundColumn>
            <asp:BoundColumn DataField="cx_pkup_address" SortExpression="cx_pkup_address" HeaderText="Pick-Up">
                <ItemStyle Wrap="False"></ItemStyle>
            </asp:BoundColumn>
            <asp:BoundColumn DataField="cx_dest_address" SortExpression="cx_dest_address" HeaderText="Destination">
                <ItemStyle Wrap="False"></ItemStyle>
            </asp:BoundColumn>
            <asp:BoundColumn DataField="cx_trip_miles" SortExpression="cx_trip_miles" HeaderText="Miles" DataFormatString="{0:N2}">
                <ItemStyle HorizontalAlign="Right"></ItemStyle>
            </asp:BoundColumn>
            <asp:BoundColumn DataField="cx_pkup_datetime" SortExpression="cx_pkup_datetime" HeaderText="Time" DataFormatString="{0:t}">
                <ItemStyle HorizontalAlign="Center"></ItemStyle>
            </asp:BoundColumn>
            <asp:BoundColumn DataField="cx_vch_wait_time_amt" SortExpression="cx_vch_wait_time_amt" HeaderText="Wait" DataFormatString="{0:C}">
                <ItemStyle HorizontalAlign="Right"></ItemStyle>
            </asp:BoundColumn>
            <asp:BoundColumn DataField="cx_vch_other_amt" SortExpression="cx_vch_other_amt" HeaderText="Other" DataFormatString="{0:C}">
                <ItemStyle HorizontalAlign="Right"></ItemStyle>
            </asp:BoundColumn>
            <asp:BoundColumn DataField="cx_vch_admin_charge_amt" SortExpression="cx_vch_admin_charge_amt" HeaderText="Admin Charge" DataFormatString="{0:C}">
                <ItemStyle HorizontalAlign="Right"></ItemStyle>
            </asp:BoundColumn>
            <asp:BoundColumn DataField="cx_vch_fare_amt" SortExpression="cx_vch_fare_amt" HeaderText="Rate" DataFormatString="{0:C}">
                <ItemStyle HorizontalAlign="Right"></ItemStyle>
            </asp:BoundColumn>
        </Columns>

        <PagerStyle ForeColor="White" CssClass="dgPager" Mode="NumericPages"></PagerStyle>
    </asp:datagrid>

剃须刀:

<table id="voucherGrid" class="dg" style="width: 800px;">
   <th class="dgHeader">
      <td>Image</td>
      <td>Call #</td>
      <td>Date</td>
      <td>Passenger</td>
      <td>Pick-Up</td>
      <td>Destination</td>
      <td>Miles</td>
      <td>Time</td>
      <td>Wait</td>
      <td>Other</td>
      <td>Admin Charge</td>
      <td>Rate</td>
   </th>
   @foreach(var voucher in Model.Vouchers) {
   <tr>
      <td>@voucher.Image</td>
      <td>@voucher.CallNum</td>
      <td>@voucher.Date</td>
      <td>@voucher.Passenger</td>
      <td>@voucher.PickUp</td>
      <td>@voucher.Destination</td>
      <td>@voucher.Miles</td>
      <td>@voucher.Time</td>
      <td>@voucher.Wait</td>
      <td>@voucher.Other</td>
      <td>@voucher.AdminCharge</td>
      <td>@voucher.Rate</td>
   </tr>
   }
</table>

你告诉我哪个看起来更容易理解?对我来说,处理 html 标签和一些额外的 @ 或

您还提到不需要单元测试。我会重新考虑这个想法。单元测试对于在生产站点发生问题之前发现问题非常有用。

【讨论】:

    【解决方案3】:

    来来回回。一次又一次。原始 HTML。 Dreamwearver。 ASP。微软字。 ASP.NET、MVC.NET。我们似乎能够打到光谱的任何一端,但中间没有甜蜜点。

    说到底,也许我们最希望说的是“好吧,至少所有的疯狂都被隔离在了视图中”。我说“希望”是因为根据我的经验,说“面向对象”比使用 OO 所暗示的所有软件原则的优点来做面向对象要容易得多。

    从长远来看,标签汤不是问题。这绝对不是 MVC 上下文中的问题。令人震惊的编码是问题所在。 MVC 部分,像连体三胞胎一样融合在一起,清楚地表明软件设计和编码原则的无能是迄今为止更大的罪行。

    像 Ruby on Rails 或 MVC.NET 这样的 MVC 框架将有助于在 MVC 范式中编码时提高编码效率;它本身不会使您的编码速度更快。如果你不知道自己在做什么,它肯定不会阻止代码维护的噩梦。

    【讨论】:

      【解决方案4】:
      1. 使用 razr 视图引擎(我的偏好,我认为它看起来更整洁)。 response.write "&lt;table&gt;" 很容易写。但是您是否在仅控制视图并且可以在不触及任何服务逻辑的情况下轻松交换或更改的文件/类中执行此操作?这让我想到了 #2。
      2. 与您的小组讨论关注点的分离,并决定每个逻辑部分的位置。依赖接触点在哪里,您的所有逻辑是否都分组在不了解视图/控制器的 DLL 中?从一开始就决定并在某处写下来。
      3. 将视图保留为视图!
      4. 不要在视图中做任何事情!
      5. 进入视图后,您只是在查看!
      6. 我有没有提到视图只是这样?

      我不嫉妒任何人使用 MVP 或任何其他范例。但是,如果您想尝试 MVC,那就做对了,您会发现重构和维护代码要容易得多。

      我的 2 美分

      【讨论】:

        【解决方案5】:

        视图层优于标签汤的优点是视图层应该将不良逻辑与不良数据隔离开来,从而更容易追踪根本原因。这不会自动发生,它必须被嵌入到视图的代码约束中。我见过的关于使用 MVC 的 HTML 视图的最佳方法如下(来自Tony Marston):

        • 将数据转换为 HTML 的代码是显示逻辑。
        • 创建或获取随后转换为 HTML 的数据的代码不是显示逻辑。

        知道是成功的一半,一致的实施是另一半。强大的力量伴随着巨大的责任,因此使用有限的命令子集有助于执行更清晰的代码。最少的语句将被打印并包含。用于生成表格、列表或表单的循环和数据绑定可以由 JavaScript 库或 XSLT 处理。变量赋值、条件逻辑和字符串操作可以在本地或全局包含中完成。其他任何事情都可以由模型或控制器处理。

        【讨论】:

          【解决方案6】:

          不要在模板代码中进行任何计算。

          看看 Django 中允许什么:https://docs.djangoproject.com/en/1.3/topics/templates/

          没有算术。不向方法传递参数。没有任何类型的定义(循环除外)。这迫使您在视图方法中执行几乎所有操作,并传入所需的任何对象和列表,从而保持干净。

          【讨论】:

            【解决方案7】:

            您可以使用 GWT、ZK、Vaadin、JSF 2 或隐藏 HTML 的东西。我不知道你说的MVC是什么意思。 Django/RoR/CakePHP Model-View-Presenter 模式有时称为 MVC 或真正的 MVC。如果您坚持使用 MVC,您的视图代码中应该只有数据绑定和事件触发器。

            我认为这是一个设计问题,而不是技术问题。

            【讨论】:

              猜你喜欢
              • 2010-12-04
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2020-08-30
              • 1970-01-01
              • 1970-01-01
              • 2012-09-21
              相关资源
              最近更新 更多