在部分视图中包含JavaScript文件

时间:2009-05-26 21:03:03

标签: javascript jquery asp.net-mvc partial-views

我想知道在部分视图中包含javascript文件的最佳做法是什么。一旦渲染,这将最终作为我的页面的html中间的js包含标记。从我的角度来看,这不是一个很好的方法。它们属于head标签,因此不应阻止浏览器一次性呈现html。

示例: 我在'PictureGallery'局部视图中使用了jquery picturegallery插件,因为这个局部视图将在几个页面上使用。只有在使用此视图时才需要加载此插件,并且我不想知道每个局部视图正在使用哪些插件...

感谢您的回答。

9 个答案:

答案 0 :(得分:28)

似乎与此问题非常相似:Linking JavaScript Libraries in User Controls

我会在这里重新提出我的答案。

我肯定会建议你不要把它们放在局部内,这正是你提到的原因。一个视图很可能会引入两个部分,这两个部分都引用了相同的js文件。在加载其余的html之前,你还获得了加载js的性能。

我不了解最佳做法,但我选择在母版页中包含任何常见的js文件,然后为特定或少量视图的其他一些js文件定义单独的ContentPlaceHolder。

这是一个示例母版页 - 它非常自我解释。

<%@ Master Language="C#" Inherits="System.Web.Mvc.ViewMasterPage" %>
<head runat="server">
    ... BLAH ...
    <asp:ContentPlaceHolder ID="AdditionalHead" runat="server" />
    ... BLAH ...
    <%= Html.CSSBlock("/styles/site.css") %>
    <%= Html.CSSBlock("/styles/ie6.css", 6) %>
    <%= Html.CSSBlock("/styles/ie7.css", 7) %>
    <asp:ContentPlaceHolder ID="AdditionalCSS" runat="server" />
</head>
<body>
    ... BLAH ...
    <%= Html.JSBlock("/scripts/jquery-1.3.2.js", "/scripts/jquery-1.3.2.min.js") %>
    <%= Html.JSBlock("/scripts/global.js", "/scripts/global.min.js") %>
    <asp:ContentPlaceHolder ID="AdditionalJS" runat="server" />
</body>

Html.CSSBlock&amp; Html.JSBlock显然是我自己的扩展,但同样,它们在他们的工作中是自我解释的。

然后说一个SignUp.aspx视图我会

<asp:Content ID="signUpContent" ContentPlaceHolderID="AdditionalJS" runat="server">
    <%= Html.JSBlock("/scripts/pages/account.signup.js", "/scripts/pages/account.signup.min.js") %>
</asp:Content>

HTHs,Charles

聚苯乙烯。这是一个关于缩小和连接js文件的后续问题: Concatenate & Minify JS on the fly OR at build time - ASP.NET MVC

编辑:根据我的其他答案的要求,我按要求实施了.JSBlock(a,b)

public static MvcHtmlString JSBlock(this HtmlHelper html, string fileName)
{
    return html.JSBlock(fileName, string.Empty);
}

public static MvcHtmlString JSBlock(this HtmlHelper html, string fileName, string releaseFileName)
{
    if (string.IsNullOrEmpty(fileName))
        throw new ArgumentNullException("fileName");

    string jsTag = string.Format("<script type=\"text/javascript\" src=\"{0}\"></script>",
                                 html.MEDebugReleaseString(fileName, releaseFileName));

    return MvcHtmlString.Create(jsTag);
}

然后神奇发生的地方......

    public static MvcHtmlString MEDebugReleaseString(this HtmlHelper html, string debugString, string releaseString)
    {
        string toReturn = debugString;
#if DEBUG
#else
        if (!string.IsNullOrEmpty(releaseString))
            toReturn = releaseString;
#endif
        return MvcHtmlString.Create(toReturn);
    }

答案 1 :(得分:1)

将脚本放在页面底部的原因是为了确保在尝试任何操作之前已加载dom。这也可以通过$(document).ready(callback)等实现。 jQuery方法。

我赞同不在html中嵌入JavaScript的观点。相反,我使用Html帮助程序创建一个空div作为服务器指令。帮助程序sig是Html.ServerData(String name,Object data);

我使用此ServerData方法获取从服务器到客户端的指令。 div标签保持干净,“data-”属性有效html5。

对于loadScript指令,我可以在我的ascx或aspx中执行类似的操作:

<%= Html.ServerData("loadScript", new { url: "pathTo.js" }) %>

或者添加另一个帮助程序来执行此操作,看起来更清洁:

<%= Html.LoadScript("~/path/to.js") %>

html输出将是:

<div name="loadScript" data-server="encoded json string">

然后我有一个可以找到任何服务器数据标记的jQuery方法: $(containingElement).serverData( “loadScript”); //返回一个类似jQuery的解码json对象数组。

客户可能看起来像这样:

var script = $(containingelement").serverData("loadScript");
$.getScript(script.url, function () {
    // script has been loaded - can do stuff with it now
});

此技术非常适合需要在加载ajax的内容中加载的用户控件或脚本。我编写的版本更多涉及处理缓存脚本,因此它们每个加载页面只加载一次并包含回调和jQuery触发器,因此您可以在它准备就绪时挂钩它。

如果有人对此版本的完整版本感兴趣(从MVC到jQuery扩展),我很乐意更详细地展示这种技术。否则 - 希望它能给某人一个接近这个棘手问题的新方法。

答案 2 :(得分:1)

今天我已经创建了自己的解决方案,完全适合这个法案。无论是否优秀的设计,都是由大家决定,但我认为我应该分享任何一种方式!

下面是我的HtmlExtensions类,它允许您在母版页中执行此操作:

<%=Html.RenderJScripts() %>

我的HtmlExtensions类:

public static class HtmlExtensions
{
    private const string JSCRIPT_VIEWDATA = "__js";

    #region Javascript Inclusions

    public static void JScript(this HtmlHelper html, string scriptLocation)
    {
        html.JScript(scriptLocation, string.Empty);
    }

    public static void JScript(this HtmlHelper html, string scriptLocationDebug, string scriptLocationRelease)
    {
        if (string.IsNullOrEmpty(scriptLocationDebug))
            throw new ArgumentNullException("fileName");

        string jsTag = "<script type=\"text/javascript\" src=\"{0}\"></script>";

#if DEBUG
        jsTag = string.Format(jsTag, scriptLocationDebug);
#else
        jsTag = string.Format(jsTag, !string.IsNullOrEmpty(scriptLocationRelease) ? scriptLocationRelease : scriptLocationDebug);
#endif

        registerJScript(html, jsTag);
    }

    public static MvcHtmlString RenderJScripts(this HtmlHelper html)
    {
        List<string> jscripts = html.ViewContext.TempData[JSCRIPT_VIEWDATA] as List<string>;
        string result = string.Empty; ;
        if(jscripts != null)
        {
            result = string.Join("\r\n", jscripts);
        }
        return MvcHtmlString.Create(result);
    }

    private static void registerJScript(HtmlHelper html, string jsTag)
    {
        List<string> jscripts = html.ViewContext.TempData[JSCRIPT_VIEWDATA] as List<string>;
        if(jscripts == null) jscripts = new List<string>();

        if(!jscripts.Contains(jsTag))
            jscripts.Add(jsTag);

        html.ViewContext.TempData[JSCRIPT_VIEWDATA] = jscripts;
    }

    #endregion

}

发生了什么事?
上面的类将使用方法扩展HtmlHelper,以将javascript链接添加到由HtmlHelper.ViewContext.TempData集合存储的集合中。在母版页的末尾,我放置了<%=Html.RenderJScripts() %>,它将jscripts集合循环到HtmlHelper.ViewContext.TempData中,并将它们渲染到输出中。

但是有一个缺点,你必须确保在添加脚本之前不要渲染脚本。例如,如果您想为css链接执行此操作,则无法使用,因为您必须将它们放在页面的<head>标记中,并且htmlhelper会在您添加之前呈现输出链接。

答案 3 :(得分:0)

这似乎是一个类似的(虽然不完全)问题。 Is it bad practice to return partial views that contain javascript?

答案 4 :(得分:0)

我的首选是创建一个继承自主Site.master的plugin.master页面。我的想法是你将这些插件填充到plugin.master中,并使用这个局部视图的8个左右的页面继承自plugin.master。

答案 5 :(得分:0)

首选方法是put scripts at the bottom,但是,如果你无法避免这种情况,那么把它们放在中间是合理的。

浏览器通常并行加载各种页面元素,但是,当浏览器下载Javascript文件时,在下载完Javascript之前,它不会并行下载任何其他页面元素。这意味着您的图像和不必等待的内容因此通常认为在底部移动脚本会更好,但如果您的脚本很小,那么我就不用担心了。

答案 6 :(得分:0)

hejdig。

玩Aspnetmvc3我现在决定将我的javascript文件包含在部分

&lt; script src =“@ Url.Content(”〜/ Scripts / User / List.js“)”type =“text / javascript”&gt;&lt; / script&gt;

此js文件特定于我的/user/List.schtml文件。

/ OF

答案 7 :(得分:0)

所以,这是一个古老的问题,但是我在尝试解决最近的问题时发现了它。我问了一个问题,并here回答了这个问题。这是该答案的副本。它类似于OP的答案,但避免使用TempData。

一种解决方案是实现Html帮助程序扩展功能,该功能只会加载一次脚本。见下文。 (这也适用于CSS和其他随附文件)。

在HttpContext中,将HtmlHelper类的扩展和私有支持类作为单例来实现。

public static class YourHtmlHelperExtensionClass 
{
    private class TagSrcAttrTracker
    {
        private TagSrcAttrTracker() { }

        public Dictionary<string, string> sources { get; } = new Dictionary<string, string>();

        public static TagSrcAttrTrackerInstance {
            get {
                IDictionary items = HttpContext.Current.Items;
                const string instanceName = "YourClassInstanceNameMakeSureItIsUniqueInThisDictionary";

                if(!items.Contains(instanceName)) 
                    items[instanceName] = new TagSrcAttrTracker();

                return items[instanceName] as TagSrcAttrTracker;
            }
        }
    }

    public static MvcHtmlString IncludeScriptOnlyOnce(this HtmlHelper helper, string urlOfScript) 
    {
        if(TagSrcAttrTracker.Instance.sources.ContainsKey(urlOfScript))
            return null;

        TagSrcAttrTracker.Instance.sources[urlOfScript] = urlOfScript;

        TagBuilder script = new TagBuilder("script");
        scriptTag.MergeAttribute("src", urlOfScript);

        return MvcHtmlString.Create(script.ToString());
    }
}

然后,将JavaScript和其他代码分成单独的文件。

.js文件内容示例

class MyClass{
    myFunction() {
        constructor(divId){
            this._divId = divId
        }

        doSomething() {
            // do something with a div with id == divId
        }
    }
}

用于部分视图的示例.cshtml文件内容

<link rel="stylesheet" type="text/css" href="~/Content/CustomCSS/MyPartialView.css"/>

<div id="@ViewBag.id">
    Some content!  Yay!
</div>

@Html.IncludeScriptOnlyOnce("/Scripts/CustomScripts/MyPartialView.js")

使用部分视图的示例.cshtml文件

...
<body>
    <h1>Here is some content!</h1>
    @Html.Partial("/Views/MyPartial.cshtml", new ViewDataDictionary() { { "id", "id_1"} })
    @Html.Partial("/Views/MyPartial.cshtml", new ViewDataDictionary() { { "id", "id_2"} })
    @Html.Partial("/Views/MyPartial.cshtml", new ViewDataDictionary() { { "id", "id_3"} })
</body>
<script>
$().ready(
    const id1Functionality = new MyClass("id_1") // forgive the poor naming :-)
    const id2Functionality = new MyClass("id_3")
    const id3Functionality = new MyClass("id_2")

    id1Functionality.doSomething();
    id2Functionality.doSomething();
    id3Functionality.doSomething();
)
</script>

部分视图可能已被包含多次,并且JavaScript与部分视图打包在一起,但是.js文件仅包含在页面中一次,因此浏览器不会抱怨MyClass被声明了多次。

答案 8 :(得分:-1)

MVC的设计者经历了很多麻烦以阻止我们使用代码隐藏,但是通过创建一个名为&lt; viewname&gt; .aspx.cs的文件并相应地修改aspx页面的inherits属性,它仍然是可以得到它们。

我会说,放置include的最佳位置是在代码隐藏中的Page_Load处理程序中(使用Page.ClientScript.RegisterClientScriptInclude)。