简体   繁体   English

在部分视图中包含 JavaScript 文件

[英]Include JavaScript file in partial views

I am wondering what the best practice is for including javascript files inside partial views.我想知道在部分视图中包含 javascript 文件的最佳实践是什么。 Once rendered this will end up as a js include tag in the middle of my page's html.一旦呈现,这将最终成为我页面 html 中间的一个 js 包含标记。 From my point of view this isn't a nice way of doing this.从我的角度来看,这不是一个很好的方法。 They belong in the head tag and as such should not prevent the browser from rendering the html in one go.它们属于 head 标签,因此不应阻止浏览器一次性渲染 html。

An example: I am using a jquery picturegallery plugin inside a 'PictureGallery' partial view as this partial view will be used on several pages.一个例子:我在“PictureGallery”部分视图中使用了一个 jquery 图片库插件,因为这个部分视图将在几个页面上使用。 This plugin only needs to be loaded when this view is used and I don't want to have to need to know which plugins each partial view is using...这个插件只需要在使用这个视图时加载,我不想知道每个局部视图正在使用哪些插件......

Thanks for your answers.感谢您的回答。

Seems very similar to this question: Linking JavaScript Libraries in User Controls似乎与这个问题非常相似: Linking JavaScript Libraries in User Controls

I'll repost my answer that that question here.我会在这里重新发布我对那个问题的回答。

I would definitely advise against putting them inside partials for exactly the reason you mention.由于您提到的原因,我绝对建议不要将它们放入部分中。 There is a high chance that one view could pull in two partials that both have references to the same js file.一个视图很可能会引入两个都引用同一个 js 文件的部分。 You've also got the performance hit of loading js before loading the rest of the html.在加载其余 html 之前,您还获得了加载 js 的性能影响。

I don't know about best practice but I choose to include any common js files inside the masterpage and then define a separate ContentPlaceHolder for some additional js files that are specific to a particular or small number of views.我不知道最佳实践,但我选择在母版页中包含任何常见的 js 文件,然后为一些特定于特定或少量视图的其他 js 文件定义一个单独的 ContentPlaceHolder。

Here's an example master page - it's pretty self explanatory.这是一个示例母版页 - 它是不言自明的。

<%@ 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 are obviously my own extensions but again, they are self explanatory in what they do. Html.CSSBlock 和 Html.JSBlock 显然是我自己的扩展,但同样,它们的作用是不言自明的。

Then in say a SignUp.aspx view I would have然后在说一个 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 HTH,查尔斯

Ps.附言。 Here is a follow up question I asked about minifying and concatenating js files: Concatenate & Minify JS on the fly OR at build time - ASP.NET MVC这是我询问的有关缩小和连接 js 文件的后续问题: 即时连接和缩小 JS 或在构建时 - ASP.NET MVC

EDIT: As requested on my other answer, my implementation of .JSBlock(a, b) as requested编辑:根据我的其他答案的要求,我按要求实现 .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);
}

And then where the magic happens...然后魔法发生的地方......

    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);
    }

The reason you would put a script at the bottom of the page is to ensure the dom has been loaded before attempting any manipulation.您将脚本放在页面底部的原因是确保在尝试任何操作之前已加载 dom。 This can also be achieved in something like the $(document).ready(callback);这也可以通过 $(document).ready(callback); 之类的东西来实现。 jQuery method. jQuery 方法。

I share the view of not putting embedded JavaScript in the html.我同意不将嵌入式 JavaScript 放入 html 的观点。 Instead I use an Html helper to create an empty div to use as a server instruction.相反,我使用 Html helper 创建一个空 div 用作服务器指令。 The helper sig is Html.ServerData(String name, Object data); helper sig 是 Html.ServerData(String name, Object data);

I use this ServerData method for instructions from the server to the client.我使用这个 ServerData 方法来获取从服务器到客户端的指令。 The div tag keeps it clean and the "data-" attribute is valid html5. div 标签保持干净,“data-”属性是有效的 html5。

For the loadScript instruction I may do something like this in my ascx or aspx:对于 loadScript 指令,我可以在 ascx 或 aspx 中做这样的事情:

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

Or add another helper to do this which looks a little cleaner:或者添加另一个助手来执行此操作,它看起来更简洁:

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

The html output would be: html 输出将是:

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

Then I have a jQuery method that can find any server data tag: $(containingElement).serverData("loadScript");然后我有一个 jQuery 方法可以找到任何服务器数据标签: $( containsElement).serverData("loadScript"); // returns a jQuery like array of the decoded json objects. // 返回一个类似 jQuery 的解码 json 对象数组。

The client may look something like this:客户端可能看起来像这样:

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

This technique is great for user controls or scripts that need to be loaded within ajax loaded content.这种技术非常适合需要在 ajax 加载内容中加载的用户控件或脚本。 The version I wrote is a little more involved handling caching scripts so they load only once per full page load and contain callbacks and jQuery triggers so you can hook into it when it is ready.我编写的版本涉及处理缓存脚本,因此它们在每个完整页面加载时只加载一次,并包含回调和 jQuery 触发器,因此您可以在它准备好时挂钩。

If anyone is interested in the full version of this (from MVC to jQuery extension) I would be happy to show off this technique in more detail.如果有人对此的完整版本(从 MVC 到 jQuery 扩展)感兴趣,我很乐意更详细地展示这种技术。 Otherwise - hopes it gives someone a new way of approaching this tricky problem.否则 - 希望它给某人一种解决这个棘手问题的新方法。

Today I've created my own solution which fits the bill perfectly.今天,我创建了自己的解决方案,完全符合要求。 Whether it is good design or not, is for you all to decide but thought I should share either way!设计好不好,由你们决定,但我认为我应该分享两种方式!

Below is my HtmlExtensions class which allows you to do this in your masterpage:下面是我的 HtmlExtensions 类,它允许您在母版页中执行此操作:

<%=Html.RenderJScripts() %>

My HtmlExtensions class:我的 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

}

What is going on?到底是怎么回事?
The class above will extend the HtmlHelper with methods to add javascript links to a collection that is being stored by the HtmlHelper.ViewContext.TempData collection.上面的类将使用方法扩展 HtmlHelper,以将 javascript 链接添加到由HtmlHelper.ViewContext.TempData集合存储的集合。 At the end of the masterpage I put the <%=Html.RenderJScripts() %> which will loop the jscripts collection inside the HtmlHelper.ViewContext.TempData and render these to the output.<%=Html.RenderJScripts() %>的末尾,我放置了<%=Html.RenderJScripts() %> ,它将在HtmlHelper.ViewContext.TempData循环 jscripts 集合并将它们呈现到输出。

There is a downside however,.. You have to ensure that you don't render the scripts before you've added them.但是,有一个缺点,.. 您必须确保在添加脚本之前不渲染它们。 If you'd want to do this for css links for example, it wouldn't work because you have to place these in the <head> tag of your page and the htmlhelper would render the output before you've even added a link.例如,如果您想对 css 链接执行此操作,则它不会起作用,因为您必须将它们放在页面的<head>标记中,并且 htmlhelper 会在您添加链接之前呈现输出。

hejdig.海迪格

Playing around with Aspnetmvc3 I decided, for now, to just include my javascript file in the partial玩弄 Aspnetmvc3 我决定,现在,只将我的 javascript 文件包含在部分

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

This js file is then specific for my /user/List.schtml file.这个 js 文件特定于我的 /user/List.schtml 文件。

/OF /的

So, this is kind of an old question, but I found it while trying to solve a recent issue.所以,这是一个老问题,但我在尝试解决最近的问题时发现了它。 I asked a question and answered it here .在这里问了一个问题并回答了它。 Here is a copy of that answer.这是该答案的副本。 It is similar to the OP's answer, but avoids using TempData.它类似于 OP 的答案,但避免使用 TempData。

One solution is to implement an Html helper extension function which will only load a script once.一种解决方案是实现一个 Html helper 扩展函数,它只会加载一次脚本。 See below.见下文。 (this can work for CSS and other included files, too). (这也适用于 CSS 和其他包含的文件)。

Implement an extension for the HtmlHelper class and a private backing class as a singleton per HttpContext.为 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());
    }
}

Then, separate the JavaScript and other code into separate files.然后,将 JavaScript 和其他代码分离到单独的文件中。

Example .js file contents示例 .js 文件内容

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

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

Example .cshtml file contents for partial view部分视图的示例 .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")

Example .cshtml file that consumes the partial view使用局部视图的示例 .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>

The partial view may have been included more than once and the JavaScript is packaged with the partial view, but the .js file is only included in the page once, hence no complaining by the browser that MyClass was declared more than once.分部视图可能被多次包含,并且 JavaScript 与分部视图一起打包,但 .js 文件只包含在页面中一次,因此浏览器不会抱怨 MyClass 被多次声明。

This seems like a similar (although not entirely) question.这似乎是一个类似(尽管不完全)的问题。 Is it bad practice to return partial views that contain javascript? 返回包含 javascript 的部分视图是不好的做法吗?

My preference will be to create a plugin.master page that inherits from your main Site.master.我的偏好是创建一个从您的主 Site.master 继承的 plugin.master 页面。 The idea is that you stuff these plugins into plugin.master and make the 8 or so pages that will use this partial view to inherit from plugin.master.这个想法是你把这些插件塞进 plugin.master 并制作 8 个左右的页面,这些页面将使用这个部分视图从 plugin.master 继承。

The preferred approach is to put scripts at the bottom , however, if you can't avoid that then it's reasonable to put them in the middle.首选方法是将脚本放在底部,但是,如果您无法避免这种情况,那么将它们放在中间是合理的。

Browsers usually load the various page elements in parallel, however, while the browser is downloading the Javascript file, it won't download any other page elements in parallel until the Javascript is done downloading.浏览器通常并行加载各种页面元素,但是,当浏览器正在下载 Javascript 文件时,它不会并行下载任何其他页面元素,直到 Javascript 下载完成。 This means that your images and what not will have to wait so it's generally considered better to move scripts at the bottom but if your script is small then I wouldn't worry about it.这意味着您的图像和其他内容将不得不等待,因此通常认为将脚本移动到底部会更好,但如果您的脚本很小,那么我不会担心。

The designers of MVC went through a lot of trouble to prevent us from using code-behinds, but by creating a file named <viewname>.aspx.cs and modify the inherits attribute of the aspx page accordingly, it is still possible to get them. MVC的设计者费了好大劲才阻止我们使用code-behinds,但是通过创建一个名为<viewname>.aspx.cs的文件,并相应地修改aspx页面的inherits属性,还是可以得到的.

I'd say, the best place to put the include would be in the Page_Load handler in the codebehind (using Page.ClientScript.RegisterClientScriptInclude).我想说,放置包含的最佳位置是代码隐藏中的 Page_Load 处理程序(使用 Page.ClientScript.RegisterClientScriptInclude)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM