我有一个表单,动态生成很多复选框(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)
}
}
}
答案 0 :(得分:5)
如果您使用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个父级有这么多行导致按需呈现问题。我们的业务逻辑允许它,因为复选框基本上是从单个父级到其子级而不是父级的链。这意味着当用户尝试工作或提交更改时,如果并非所有父母都满载,则无关紧要。这是一种解决方法,因为客户绝对不会接受分页,但也不能确保他们以合理的数量保持其开放记录!
答案 1 :(得分:5)
另外需要注意的是使用客户端验证时的Html.HiddenFor。
当您使用 jquery.validate.unobtrusive.js 使用MVC项目中提供的默认包时,您将使用如下脚本呈现:
@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" })
如果您不需要客户端验证,请确保不包含该捆绑包。
更多阅读here。
答案 2 :(得分:0)
要进行调试,请使用Chrome开发者工具。然后查看网络选项卡并单击“保留导航中的记录”。 https://developers.google.com/chrome-developer-tools/docs/network#network_panel_overview
完成后,请提交表单并查看发布的内容。
答案 3 :(得分:0)
在提交表单之前取消不打扰的验证也可以。
@onsubmit = "$('form').removeData('validator').removeData('unobtrusiveValidation'); return true;"