C# MVC在布局代码之前执行视图代码并破坏我的脚本顺序

C# MVC在布局代码之前执行视图代码并破坏我的脚本顺序,c#,javascript,html,asp.net-mvc-3,razor,C#,Javascript,Html,Asp.net Mvc 3,Razor,我正在尝试将我的所有javascript包含移到页面底部。我正在使用MVC和Razor。我已经编写了一个助手方法来注册脚本。它使脚本保持注册顺序,并排除重复 @{ Html.RegisterScript("/scripts/someFile.js"); } 然后在结束体标记之前,我做了如下操作: @Html.RenderScripts() </body> 然后我有InnerView.cshtml和_Master.cshtml作为它的布局,在我的视图中 @{ Html

我正在尝试将我的所有javascript包含移到页面底部。我正在使用MVC和Razor。我已经编写了一个助手方法来注册脚本。它使脚本保持注册顺序,并排除重复

@{ Html.RegisterScript("/scripts/someFile.js"); }
然后在结束体标记之前,我做了如下操作:

    @Html.RenderScripts()
</body>
然后我有InnerView.cshtml和_Master.cshtml作为它的布局,在我的视图中

@{
   Html.RegisterScript("script3.js");
}
脚本按以下顺序呈现:脚本3、脚本1、脚本2


我有没有办法在内部视图之前强制执行布局?我试着把所有的东西都搬来搬去,所有内在的观点似乎都是先执行的。也许这是意料之中的事?如果是这样的话,对我来说,完成我想做的事情的最佳方式是什么?

不,这是他们执行的顺序,从最内部到最外部。我建议使用某种后进先出(last-in-first-out)集合来保存脚本,然后将它们从堆栈中弹出以渲染它们;这样,最后添加的脚本(即布局中的脚本)将首先呈现

编辑


我写的解决了这个问题。

没有办法改变MVC呈现页面的顺序(据我所知),但您可以尝试做的是更新呈现脚本功能,以指示版面何时注册脚本。例如,您的布局可能是:

@{
   Html.StartLayoutScriptRegistration();
   Html.RegisterScript("script1.js");
   Html.RegisterScript("script2.js");
}

这将触发您的系统,在调用
startayoutscriptregistration()
之后注册的脚本应首先呈现,然后是在调用“startayoutscriptregistration()”之前注册的视图脚本注册

通过跟踪每个视图的脚本,我最终解决了这个问题。基本上我有一个

Dictionary<string, SortedList<int, string>> registeredScripts;
字典注册脚本;

SortedList视图顺序;
每次注册脚本时,我都会传入脚本路径和视图名称(从ViewDataContainer.ToString()检索)。我跟踪在viewOrder对象中查看视图的顺序。然后,我使用字典跟踪每个视图的脚本。关键是视图名称。当需要渲染所有内容时,我反转viewOrder SortedList并按view写出所有脚本。好的一点是,脚本是按视图顺序排列的,视图顺序一旦颠倒就正确了


我做了一个糟糕的解释,但由于版权原因,我不能共享代码。

首先,创建两个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;
    }
Html.RegisterScript("script3.js", Position.Bottom);
Html.RegisterScript("script1.js", Position.Top);
Html.RegisterScript("script2.js", Position.Top);
最后,在Razor模板中呈现脚本:

@Html.RenderScripts()
结果将按优先级排序,并在脚本标记内呈现:

<script type="text/javascript" src="script1.js"></script>
<script type="text/javascript" src="script2.js"></script>

作为脚本依赖性的精确解决方案,您可以提供一些帮助方法:

指定依赖项

Html.RegisterScript("script3.js", depsOn: new string[] {"script1.js", "script2.js"});
指定相对优先级:

    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;
    }
Html.RegisterScript("script3.js", Position.Bottom);
Html.RegisterScript("script1.js", Position.Top);
Html.RegisterScript("script2.js", Position.Top);

通过这种方式,您可以在大多数情况下获得所需的控制。

放置在@部分的代码总是按照您期望的顺序执行,因此您可以利用这一事实:

在您的_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中的任何位置。这也适用于嵌套布局

我考虑过这一点,但是对于开发人员来说,用这种方法以相反的顺序注册脚本是很奇怪的。例如,如果在同一页上,script1依赖于script2,我必须以相反的顺序注册它们,所以这对我来说并不适用。可能是一个堆栈。因此,一个视图将其所有项按顺序排列在一个列表中,然后调用.Push(SomeListInNaturalOrder),因为它只在视图之间需要反转。我想这就是你在另一个答案中所做的。我喜欢这个。。。虽然我最终提出了一个不同的解决方案
@{
   Html.RegisterScript("script1.js");
   Html.RegisterScript("script2.js");
}

@RenderSection("references", required: false)
@section references {@{
       Html.RegisterScript("script3.js");
}}