关于c#:将所有JavaScript保存在页面底部的简洁模式是什么?

What is a clean pattern for keeping all the JavaScript in the bottom of my page?

我们为不同的页面提供了一个嵌套的布局。例如:

Master.cshtml

1
2
3
4
5
<!DOCTYPE html>
<html>
   <head>...</head>
   <body>@RenderBody()<body>
</html>

Question.cshtml

1
2
3
4
5
  ... lot of stuff ...
  @Html.Partial("Voting", Model.Votes)

<script type="text/javascript">
  ... some javascript ..

Voting.cshtml

1
2
3
4
  ... lot of stuff ...

<script type="text/javascript">
  ... some javascript ..

这一切都很好,但我希望在所有内容之后,将所有的javascript块都推送到页面的底部。

有没有一种方法可以在嵌套的部分中定义一个magic指令,使各种脚本标记在页面底部按顺序呈现?

例如,我可以创建一个魔法助手来捕获所有JS块,然后获取顶层布局来呈现它吗?

Voting.cshtml

1
2
3
4
5
6
7
  ... lot of stuff ...

@appendJSToFooter{
   <script type="text/javascript">
     ... some javascript ..
   
}


大约一年前,我通过创建一个助手来在ViewContext.TempData中注册脚本,为这个问题提出了一个相对简单的解决方案。我的初始实现(我正在重新思考)只是输出到各种引用脚本的链接。不完美,但这里是我当前实现的一个演练。

在分部中,我按名称注册关联的脚本文件:

1
@Html.RegisterScript("BnjMatchyMatchy")

然后在主页上,我调用一个方法来迭代注册的脚本:

1
@Html.RenderRegisteredScripts()

这是当前助手:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
public static class JavaScriptHelper
{
    private const string JAVASCRIPTKEY ="js";

    public static void RegisterScript(this HtmlHelper helper, string script)
    {
        var jScripts = helper.ViewContext.TempData[JAVASCRIPTKEY]
            as IList<string>; // TODO should probably be an IOrderedEnumerable

        if (jScripts == null)
        {
            jScripts = new List<string>();
        }

        if (!jScripts.Contains(script))
        {
            jScripts.Add(script);
        }

        helper.ViewContext.TempData[JAVASCRIPTKEY] = jScripts;
    }

    public static MvcHtmlString RenderRegisteredScripts(this HtmlHelper helper)
    {
        var jScripts = helper.ViewContext.TempData[JAVASCRIPTKEY]
            as IEnumerable<string>;

        var result = String.Empty;

        if (jScripts != null)
        {
            var root = UrlHelper.GenerateContentUrl("~/scripts/partials/",
                    helper.ViewContext.HttpContext);

            result = jScripts.Aggregate("", (acc, fileName) =>
                String.Format("<script src="{0}{1}.js"" +
                   "type="text/javascript">

"
, root, fileName));
        }

        return MvcHtmlString.Create(result);
    }
}

正如我的托多所指出的(我应该讨论一下),你可以很容易地修改这个,使用一个IORDerenumerable来保证订单。

正如我所说的不完美,输出一堆script src标签肯定会造成一些问题。我一直在潜伏,因为你关于jquery税的讨论已经和史蒂夫·索德斯、StackOverflow、Twitter和你的博客展开。无论如何,它激励我重新编写这个助手来读取脚本文件的内容,然后将它们以自己的script标记而不是链接标记转储到呈现的页面。这种更改应该有助于加速页面呈现。


我意识到这艘船已经航行了很多次,但由于我有某种解决方案(尽管不是完美的),我想我会分享它。

我写了一篇关于我使用的脚本呈现方法的博客文章。在那篇文章中,我提到了迈克尔·J·瑞安写的一个图书馆,我为自己的目的做了一些调整。

使用这种方法,可以在任何地方使用如下方法呈现脚本:

1
2
3
4
5
6
7
@Html.AddClientScriptBlock("A script", @"

    $(function() {
        alert('Here');
    });

"
)

然后在布局页面中,在结束正文标记之前使用此调用触发它的输出:

1
@Html.ClientScriptBlocks()

使用此方法在Visual Studio中无法获得IntelliSense。如果我诚实的话,我并不建议使用这种技术;我宁愿为调试点使用单独的JS文件,并使用HTML数据属性来驱动任何动态行为。但如果有用的话,我想我会分享它。空手警告等

以下是相关链接的列表:

  • 我的博客帖子
  • Michael J.Ryan的博客帖子
  • Github上的帮助者

  • 如果您遇到的情况是无法改进现有的结构(例如,您有MVC和旧的WebForms页面,所有地方都包含javascript标记等等),那么您可以看看我在一个对输出进行后处理的HTTP模块上所做的一些工作,就像在Apache中所做的mod pagespeed一样。不过还是很早的时候。

    https://github.com/teun/resourcecombinator

    如果您仍然可以选择以预先定义的方式包含脚本,那可能更好。

    编辑:Sam提到的项目(见评论)实际上在很大程度上是重叠的,而且更加成熟。我从Github中删除了ResourceCombinator项目。


    这有点老生常谈,但是如果您的目标是对现有视图(除了移动渲染的脚本之外)进行最小的更改,那么我过去所做的方法(特别是在Web表单中,但也适用于MVC)就是用一个将脚本推到底部的方法覆盖TextWriter

    基本上,您只需编写一个TextWriter实现,并将其连接到您的MVC基页,该基页查找


    那么HTML响应过滤器呢,它可以修改最终的HTML。在特定位置之前找到所有脚本标记,并将它们粘贴到正文的末尾。它还可以改进数据表的呈现,通过最初将它们标记为隐藏,并让JS显示出来。

    不需要对实际代码进行干预或更改。


    山姆,

    我之所以写门户网站是因为这个原因:http://nuget.org/packages/portal你基本上把

    1
    @Html.PortalOut()

    在主视图/布局视图中,从视图或类似的部分视图中填充HTML代码

    1
    @Html.PortalIn(@<text> $(function() { alert('Hi'); }); </text>)

    通过指定输入和输出键,可以使用多个"门户"。现在,它按代码的顺序添加代码(先渲染部分,从"上到下")。如果你在同一个视图中,你必须处理从上到下的限制,也就是说,你不能在"in"之前有一个"out"门户,但是从视图发送到布局时这不是一个问题。在portal.cs文件中有更多的示例。


    我谦虚的选择是把这类事情留给专业人员:-)看看ControlJS,一个加载外部JavaScript文件的lib,而不中断页面处理(异步、延迟、按需等)。有了ControlJS,您不必介意将加载代码放在"哪里",它们都将在末尾或按需加载。

    自由党作者SteveSouders的这篇演讲对这个问题(以及解决方案)进行了很好的概述。


    我们使用Telerik(GPL开源)ASP.NET MVC库中的脚本注册器方法。

    只需在下面的任何视图/部分页面等中包含您的javascript。Telerik库处理在最终输出页面的底部呈现所有这些内容。

    1
    2
    3
    4
    5
    6
    7
    <%
    Html.Telerik().ScriptRegistrar()
                 .Scripts(scripts => scripts.AddSharedGroup('lightbox')
                 .OnDocumentReady(() => { %>
       <%-- LIGHTBOX  --%>
       $('.images a').lightBox();
    <% });%>

    它还负责将多个CSS和JS包含组合成单个请求。

    http://www.telerik.com/products/aspnet-mvc.aspx


    我建议你不要把脚本直接放到你的页面上。

    相反,将javascript拉到一个单独的.js文件中,然后在头中引用它。

    我知道这并不完全是你想要的,但是我假设你这样做是为了SEO的目的,这应该和把脚本放在页面底部有同样的效果。