Is there a way to determine an ASP.Net MVC Bundle

2019-04-11 22:03发布

问题:

Here is the situation :

On a MVC application I have a partial that is rendering a script bundle. this partial is rendered several times.

Is there a built-in way to determine an specific Bundle is rendered before in some place on page ?

Thanks professionals

Update : here is the code for clearing the condition

main layout :

<html>
  <body>
    @if(someCondition){
      @Html.RenderPartial("SamePartial")
      @Html.RenderPartial("SamePartial")
      @Html.RenderPartial("SamePartial")
    }
  </body>
</html>

and this is inside partial :

<div>Blah Blah Blah</div>

@Scripts.Render("~/bundles/JusNeededInsidePartial")
@Styles.Render("~/Themes/styles/JusNeededInsidePartial")

partial is rendered several times. I need a way to render bundles 1 time only

回答1:

Define a Razor section:

@section MySpecialScripts {
    @Scripts.Render("~/bundles/MySpecialScriptsBundle")
}

in views where you need such scripts and add the following to the main layout:

@if (IsSectionDefined("MySpecialScripts")) {
    RenderSection("MySpecialScripts")
}


回答2:

I'd be a bit leery about putting bundles inside your partial views like you are doing.

For best performance, I'd suggest putting scripts at the bottom of your page (ref: http://developer.yahoo.com/blogs/ydn/high-performance-sites-rule-6-move-scripts-bottom-7200.html)

If your bundles contain stylesheets, you're going to be putting them in the body, which is still valid, but I like to organize them so that they're all together.

Typically what I would do is add this to my _Layout.cshtml right before the closing </head> tag:

 @RenderSection("Head", required: false)

Then, in my View, I could have:

@Html.RenderPartial("SamePartial")
@Html.RenderPartial("SamePartial")
@Html.RenderPartial("SamePartial")

@section head{
    @Scripts.Render("~/bundles/myBundle")
}

And if I wanted to do something similar on another View:

@Html.RenderPartial("OtherPartial")
@Html.RenderPartial("OtherPartial")

@section head{
    @Scripts.Render("~/bundles/myOtherBundle")
}

I know this is a bit different than what you were asking for, but I think there's value in the flexibility of this solution.

And if you're loading things dynamically, you could always put a simple condition in your section:

@section head{
    @if(SomeCondition){
        @Scripts.Render("~/bundles/modernizr")
    }
}


回答3:

You can do the check in the JavaScript scripts themselves:

In JavaScript you can check if something has been previously defined, and only define it if it hasn't.

For example:

if (typeof flag === 'undefined') {
    // variable is undefined
    flag = {};
    // Here type the code that must happen only once
}

NOTE: the flag name is a global var. To avoid collisions, you'd "namespace", and use specific names for each script file like this:

if (typeof myScripts === 'undefined') {
   myScripts = {};
}

if (typeof myScripts.MySpecialScripts=== 'undefined')
{
   myScripts.MySpecialScripts = '';
   // Add the "MySpecialScripts.js" code here
}

However, I'd render the script bundle outside the partials to ensure it's available to them. In this way you can ensure that it's rendered only once and available to all your partials.

In fact, unless it's a very large .js file, why not include it in your general bundle?

Yep, I know you feel tha your project is "better organized" if you include the scripts on the view which needs them, but that's not a good idea: you have to control if they have yet been included or not, you cand' move them to the bottom of the page if you want to and so on.

If you still want to keep your original idea you can use a the client dependency framework. It's not exactly what you want to do, but it's the best available solution. When you use this framework, you need to declare the dependencies of you components (Views) using different methods, like this:

  @{Html.RequiresJs("~/myscript.js");}

Anyway, as I wrote in my comment, the problem with this kind of solution is that if you start using AJAX there is no single way to get sure that you always get the desired scripts, and that they are available on client side when you need them. Unless you use some variation of the first client-side solution that I wrote in this answer (there are better solutions based on this basic concept). Let me insist: the best solution is always to have everything ready on the client side (unless it's a really huge project that have hundreds of big js files). Remember that the js file is loaded once, and cached for later use in the browser, so downloading a big .js file it's not such a big problem.

And please, even if you think you need a server side solution, don't discard a client side solution. Remember that, even if you server the js from the server, that's 'client side' code, so, why not use a 'client side solution' for your 'client side code' problem?

Still anothe solution: ypu can use PageData to share information between views, layouts, and partial views. When you need to add a script in any of this places, use a flag in PageData. If the flag is not present, render the script, and set the flag. If the flag is present, don't render the script. In this way, the script will be rendered only the first time.

if (PageData["JusNeededInsidePartial"] == null) // Check the flag
{
    @Scripts.Render("~/bundles/JusNeededInsidePartial");
    PageData["JusNeededInsidePartial"] = ""; // Set the flag
}

The problem with AJAX still exists, because if you render the partial by AJAX, it will load the script in the client side everything the partial view is loaded by AJAX.