joe*_*oel 3 asp.net-mvc performance razor
我有一个动态生成很多复选框(700+)的表单.表单在IE中加载速度非常慢(但几乎没有在chrome中加载),当我发布表单时,它几乎锁定了我的Web浏览器.
我该怎么调试这个,或者我做错了导致这个性能问题?或者这是一个巨大的形式,我应该尝试将其拆分.
这是我的控制器:
public ActionResult Create(int id)
    { 
            var model = new TaskRequestViewModel
                {
                    Task = new Task(),
                    Components = db.Component.ToList()
                       …. 
                 }
            return View(model);           
    }
为了简要解释我的模型,我有大约10个组件,每个组件有大约10个子组件,每个子组件有大约10个选项(复选框)可用.导致700多个字段(然后是一些隐藏的字段).当复选框较少时(100),它工作正常.
我的观点是这样的(有3个嵌套循环):
           @for (int cI = 0; cI < Model.Components.Count; cI++)
           {  
                    @Html.HiddenFor(x => Model.Components[cI].ComponentId)  
                    @for (int scI = 0; scI < Model.Components[cI].SubComponents.Count; scI++)
                    { 
                            @Html.DisplayFor(x => Model.Components[cI].SubComponents[scI].Name)                                 
                            @Html.HiddenFor(x => Model.Components[cI].SubComponents[scI].SubComponentId)
                            @for (int t = 0; t < Model.Components[cI].SubComponents[scI].TaskTypes.Count; t++)
                            {
                                @Html.HiddenFor(x => Model.Components[cI].SubComponents[scI].TaskTypes[t].SubComponentTaskTypeId)
                                @Html.HiddenFor(x => Model.Components[cI].SubComponents[scI].TaskTypes[t].TaskTypeId)
                                @Html.CheckBoxFor(x => Model.Components[cI].SubComponents[scI].TaskTypes[t].Active)
                           } 
                    }
          }
如果您使用Visual Studio性能分析器,您将看到第二级索引访问器,您的渲染时间开始向上拍摄.HiddenFor,EditorFor,LabelFor等扩展可以非常强烈,因为它们反映在您的模型上,以确定生成HTML时数据注释的ID,名称和存在.
随着渲染花费这么长时间,浏览器往往会冻结,因为它开始得到一些HTML但没有得到它所以它暂停.即使在加载了所有HTML之后,任何尝试在加载时运行的JavaScript库都需要进行评估(可能在很多字段上 - 这取决于您的业务逻辑,如果您使用的东西如Modernizr,Html5Shi [v | m],当您提交时,如果您正在使用不显眼的验证,则可能会冻结每个字段的枚举并检查验证以及任何OnSubmit类型逻辑.
加快渲染速度:
当您的模型使用多个级别的子级时,此执行会因许多记录而变得非常昂贵.不幸的是,只是将每个级别移动到其自己的部分视图并传入父集合并不能100%解决问题.虽然它确实加快了速度,但您的模型名称不再正确生成.
例如,你的一个孩子的输入标签上的Name属性看起来像"Components [17] .SubComponents [33] .TaskTypes [5] .SubComponentTaskTypeId",基于你的模型具有完整的上下文链.如果拆分为局部视图并将模型指定为TaskType,则当您尝试在第18个组件上的第34个SubComponent上渲染第6个时,该名称现在将为"TaskType.SubComponentTaskTypeId"(假设您的模型是在调用的TaskType中)一个循环)或"TaskType [5] .SubComponentTaskTypeId"(假设你的模型是IEnumerable一次调用).
这意味着模型绑定器将不再知道如何将提交的表单转换回完整对象.
对于我们,我们使用编辑器模板解决了这个问 这些基本上是部分视图,但父级的上下文存储为Prefix属性,该属性自动预先设置为字段名称.这是一个更好的性能(如果你正在做那么多领域,仍然不是很好),名称仍然会以模型绑定器在发布到你的动作时理解的方式生成.
如果您为属性使用编辑器模板,则只需使用
@for (int t = 0; t < Model.Components[cI].SubComponents[scI].TaskTypes.Count; t++)
{
    @Html.EditorFor(m => m.TaskType[t])
}
如果它本身位于从SubComponents [n]调用的编辑器模板中,它本身就是一个从Components [m]调用的编辑器模板,那么你将保持全名解析.这是因为每个级别都会抓取前缀一次并存储它,同时渲染子项,从而无需反复反映模型以生成名称.
作为奖励你的观点,因为更具可读性,你的显示逻辑现在被关注(数据类型)分开而不是混合在一起.
要在提交时诊断冻结:
您需要运行一些JavaScript性能分析器来查看正在运行的内容以及何时运行.查看正在加载的库以查看是否需要它们.如果你已经在你的bundle和_layout中获得了对Modernizr的默认MVC引用,那么你将从中受到巨大的打击.这部分调试很难指导,因为它依赖于所使用的客户端库.
编辑:在与一个有这种情况的同事交谈时(许多行3级儿童带有文本框,3个输入,3个链接和每行5个标签)他仍然无法为特定用户呈现这么多记录报告异常高的行.数据太多,浏览器变得不稳定,偶尔会崩溃.
他最终使用JavaScript渲染模板来帮助解决问题.基本上他加载了每一行的父级以及一个Expand链接.单击它时,他将使用AJAX为子项获取JSON对象,并使用渲染模板生成HTML.当然,你必须小心地为模型绑定器正确设置id和name属性以便在提交时选择它们但是对他来说要好得多,因为没有1个父级有这么多行导致按需呈现问题.我们的业务逻辑允许它,因为复选框基本上是从单个父级到其子级而不是父级的链.这意味着当用户尝试工作或提交更改时,如果并非所有父母都满载,则无关紧要.这是一种解决方法,因为客户绝对不会接受分页,但也不能确保他们以合理的数量保持他们的开放记录!
另外需要注意的是使用客户端验证时的Html.HiddenFor.
当您使用MVC项目中提供的默认包使用jquery.validate.unobtrusive.js时,您将使用如下脚本呈现:
@Scripts.Render("~/bundles/jqueryval")
(注意:您可能已经更改了名称,这是默认值)
当你有这个,任何Html.HiddenFor将像这样呈现:
<input data-val="true" data-val-required="The XYZ field is required." name="XYZ" type="hidden" value="">
不用说,这些字段不需要启用客户端验证.用户无法看到它们,也无法与它们进行交互,并且应始终由模型填充.
解决这个问题的方法很简单:
@Html.HiddenFor(x => x.XYZ, new { data_val = "false" })
如果您不需要客户端验证,请确保不包含该捆绑包.
在这里阅读更多内容.