71

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

一个例子: 我在一个'PictureGallery'部分视图中使用一个jquery图片库插件,因为这个部分视图将在几个页面上使用。仅在使用此视图时才需要加载此插件,并且我不想知道每个部分视图正在使用哪些插件...

感谢您的回答。

4

9 回答 9

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 和 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>

HTH,查尔斯

附言。这是我询问的关于缩小和连接 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);
    }
于 2009-05-26T22:01:53.533 回答
1

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

我同意不将嵌入式 JavaScript 放入 html 的观点。相反,我使用 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"); // 返回解码后的 json 对象的类似 jQuery 的数组。

客户端可能看起来像这样:

var script = $(containingelement").serverData("loadScript");
$.getScript(script.url, function () {
    // 脚本已经加载 - 现在可以用它做事了
});

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

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

于 2010-06-05T00:22:39.137 回答
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 会在您添加链接之前呈现输出。

于 2010-11-04T11:02:36.803 回答
0

这似乎是一个类似(尽管不完全)的问题。 返回包含 javascript 的部分视图是不好的做法吗?

于 2009-05-26T21:14:55.140 回答
0

我的偏好是创建一个从您的主 Site.master 继承的 plugin.master 页面。这个想法是你把这些插件塞进plugin.master 并制作8 个左右的页面,这些页面将使用这个部分视图从plugin.master 继承。

于 2009-05-26T21:16:20.023 回答
0

首选方法是将脚本放在底部,但是,如果您无法避免,那么将它们放在中间是合理的。

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

于 2009-05-26T21:23:25.360 回答
0

海迪格。

使用 Aspnetmvc3 我决定,现在,只在部分中包含我的 javascript 文件

<script src="@Url.Content("~/Scripts/User/List.js")" type="text/javascript"></script>

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

/的

于 2010-12-02T10:56:39.450 回答
0

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

一种解决方案是实现一个 Html 助手扩展功能,它只会加载一次脚本。见下文。(这也适用于 CSS 和其他包含的文件)。

实现 HtmlHelper 类的扩展和私有支持类作为每个 HttpContext 的单例。

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 被多次声明。

于 2020-05-28T14:55:18.983 回答
-1

MVC 的设计者费了很大的劲阻止我们使用代码隐藏,但是通过创建一个名为 <viewname>.aspx.cs 的文件并相应地修改 aspx 页面的继承属性,仍然可以得到它们.

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

于 2009-05-26T21:09:53.763 回答