MVC 在布局代码之前执行视图代码并破坏我的脚本顺序
Posted
技术标签:
【中文标题】MVC 在布局代码之前执行视图代码并破坏我的脚本顺序【英文标题】:MVC executing view code before layout code and ruining my script order 【发布时间】:2011-12-22 04:42:44 【问题描述】:我正在尝试将我所有的 javascript 包含移动到页面底部。我将 MVC 与 Razor 一起使用。我写了一个辅助方法来注册脚本。它按照它们注册的顺序保存脚本,并且不包括欺骗。
@ html.RegisterScript("/scripts/someFile.js");
然后在结束正文标记之前,我做了这样的事情:
@Html.RenderScripts()
</body>
如果脚本全部注册在一个页面上,这一切都可以正常工作。如果我在布局中注册一些脚本,在我的内部视图中注册一些脚本,事情就会出错。基本上,内部视图在布局之前执行。所以事件虽然内部视图中的脚本依赖于外部视图中的脚本,但它们是事先注册的。
所以如果我有 _Master.cshstml 并且它有
@
Html.RegisterScript("script1.js");
Html.RegisterScript("script2.js");
然后我有 InnerView.cshtml 和 _Master.cshtml 作为它的布局,在我有的视图中
@
Html.RegisterScript("script3.js");
脚本按以下顺序呈现:script3、script1、script 2。
有什么办法可以强制布局在内部视图之前执行?我已经尝试过在整个地方移动东西,并且内部视图似乎总是首先执行。也许这是意料之中的?如果是这样,对我来说完成我想做的事情的最佳方式是什么?
【问题讨论】:
【参考方案1】:不,这是他们执行的顺序,从最里面到最外面。我建议使用某种 LIFO(后进先出)集合,例如 Stack<T>
来保存脚本,然后将它们从堆栈中弹出以呈现它们;这样,最后添加的脚本,即布局中的脚本,将是最先呈现的脚本。
编辑
我写了 a nuget package that handles rendering scripts from Partial views and Templates 来解决这个问题。
【讨论】:
我考虑过这一点,但是对于开发人员来说,使用该方法以他们所依赖的相反顺序注册脚本很奇怪。例如,如果在同一页面上 script1 依赖于 script2,我必须以相反的顺序注册它们,所以这对我来说真的不起作用。 可能是一个堆栈>。因此,视图将所有项目按顺序排列在列表中,然后调用 .Push(someListInTheNaturalOrder),因为它只需要在视图之间进行反转。我认为这基本上就是您在其他答案中所做的。【参考方案2】:我最终通过跟踪每个视图的脚本来解决这个问题。基本上我保持一个
Dictionary<string, SortedList<int, string>> registeredScripts;
还有一个
SortedList<int, string> viewOrder;
每次注册脚本时,我都会传入脚本路径和视图名称(从 ViewDataContainer.ToString() 中检索)。我跟踪在 viewOrder 对象中查看视图的顺序。然后我使用字典来跟踪每个视图的脚本。关键是视图名称。当需要渲染所有内容时,我反转 viewOrder SortedList 并按视图写出所有脚本。好的一点是,脚本是按视图排序的,并且一旦反转视图顺序是正确的。
我做了很糟糕的解释,但出于版权原因,我不能分享代码。
【讨论】:
【参考方案3】:没有办法更改 MVC 呈现页面的顺序(据我所知),但您可以尝试更新您的呈现脚本功能,以指示布局何时注册其脚本。例如,您的布局可能是:
@
Html.StartLayoutScriptRegistration();
Html.RegisterScript("script1.js");
Html.RegisterScript("script2.js");
这将触发您的系统,应该首先呈现在调用 StartLayoutScriptRegistration()
之后注册的脚本,然后是在调用 'StartLayoutScriptRegistration()' 之前注册的视图脚本注册..
【讨论】:
我喜欢这个......虽然我最终想出了一个不同的解决方案【参考方案4】:首先,创建 2 个 HTML 助手。一种按优先级添加脚本:
public static MvcHtmlString AddScript(this HtmlHelper htmlHelper, string script, int executionSequence = int.MaxValue)
Dictionary<int, List<string>> scripts = new Dictionary<int, List<string>>();
if (htmlHelper.ViewContext.HttpContext.Items["_scripts_"] != null)
scripts = htmlHelper.ViewContext.HttpContext.Items["_scripts_"] as Dictionary<int, List<string>>;
if (!scripts.ContainsKey(executionSequence))
scripts.Add(executionSequence, new List<string>());
string tag = string.Format("<script type=\"text/javascript\" src=\"0\"></script>", script);
scripts[executionSequence].Add(tag);
htmlHelper.ViewContext.HttpContext.Items["_scripts_"] = scripts;
return MvcHtmlString.Empty;
还有一个用于将这些脚本写入您的视图:
public static IHtmlString RenderScripts(this HtmlHelper htmlHelper)
StringBuilder sb = new StringBuilder();
foreach (object key in htmlHelper.ViewContext.HttpContext.Items.Keys)
if (key.ToString() == "_scripts_")
Dictionary<int, List<string>> scripts = htmlHelper.ViewContext.HttpContext.Items[key] as Dictionary<int, List<string>>;
foreach (int index in scripts.Keys.OrderBy(x => x))
foreach (string script in scripts[index])
if (script != null)
sb.AppendLine(script);
return MvcHtmlString.Create(sb.ToString());
然后将脚本添加到您的局部视图中:
@Html.AddScript("script2.js", 2)
@Html.AddScript("script1.js", 1)
最后,在 Razor 模板中渲染脚本:
@Html.RenderScripts()
结果将按优先级排序并呈现在脚本标签内:
<script type="text/javascript" src="script1.js"></script>
<script type="text/javascript" src="script2.js"></script>
【讨论】:
【参考方案5】:作为脚本依赖的准确解决方案,您可以提供一些帮助方法:
指定依赖项
Html.RegisterScript("script3.js", depsOn: new string[] "script1.js", "script2.js");
指定相对优先级:
Html.RegisterScript("script3.js", Position.Bottom);
Html.RegisterScript("script1.js", Position.Top);
Html.RegisterScript("script2.js", Position.Top);
这样您就可以在大多数情况下获得所需的控制权。
【讨论】:
【参考方案6】:@section 部分中的代码总是按您预期的顺序执行,因此您可以利用这一事实:
在你的_Master.cshtml中写:
@
Html.RegisterScript("script1.js");
Html.RegisterScript("script2.js");
@RenderSection("references", required: false)
在你的 InnerView.cshtml 中:
@section references @
Html.RegisterScript("script3.js");
这将导致您想要的包含顺序:script1、script2、script3
注意:由于“参考”部分仅包含一个代码块且没有真正的 Html 输出,因此您可以将 @RenderSection 部分放在 _Master.cshtml 中的任何位置。这也适用于嵌套布局!
【讨论】:
以上是关于MVC 在布局代码之前执行视图代码并破坏我的脚本顺序的主要内容,如果未能解决你的问题,请参考以下文章
下拉列表的 jQuery 在布局 ASP.NET MVC 5 中不起作用