Wie kann ich eine explizite ScriptBundle-Include-Reihenfolge angeben?

91

Ich probiere die MVC4 System.Web.Optimization 1.0 ScriptBundle-Funktion aus .

Ich habe die folgende Konfiguration:

public class BundleConfig
{
    public static void RegisterBundles(BundleCollection bundles)
    {
        // shared scripts
        Bundle canvasScripts =
            new ScriptBundle(BundlePaths.CanvasScripts)
                .Include("~/Scripts/modernizr-*")
                .Include("~/Scripts/json2.js")
                .Include("~/Scripts/columnizer.js")
                .Include("~/Scripts/jquery.ui.message.min.js")
                .Include("~/Scripts/Shared/achievements.js")
                .Include("~/Scripts/Shared/canvas.js");
        bundles.Add(canvasScripts);
    }
}

und die folgende Ansicht:

<script type="text/javascript" src="@Scripts.Url(BundlePaths.CanvasScripts)"></script>

wo BundlePaths.CanvasScriptsist "~/bundles/scripts/canvas". Es macht dies:

<script type="text/javascript" src="/bundles/scripts/canvas?v=UTH3XqH0UXWjJzi-gtX03eU183BJNpFNg8anioG14_41"></script>

So weit so gut, außer ~/Scripts/Shared/achievements.jsdas erste Skript in der gebündelten Quelle. Es hängt von jedem Skript ab, das zuvor in der ScriptBundle. Wie kann ich sicherstellen, dass die Reihenfolge eingehalten wird, in der ich dem Bundle Include-Anweisungen hinzufüge?

Aktualisieren

Dies war eine relativ neue ASP.NET MVC 4-Anwendung, die jedoch auf das Pre-Release-Paket des Optimierungsframeworks verwies. Ich habe es entfernt und das RTM-Paket von http://nuget.org/packages/Microsoft.AspNet.Web.Optimization hinzugefügt . Bei der RTM-Version mit debug = true in web.config werden @Scripts.Render("~/bundles/scripts/canvas")die einzelnen Skript-Tags in der richtigen Reihenfolge gerendert .

Mit debug = false in web.config verfügt das kombinierte Skript zuerst über das Skript settings.js. Da es sich jedoch um eine später aufgerufene Funktionsdefinition (Objektkonstruktor) handelt, wird es fehlerfrei ausgeführt. Vielleicht ist der Minifier klug genug, um Abhängigkeiten herauszufinden?

Ich habe auch die IBundleOrdererImplementierung ausprobiert, die Darin Dimitrov mit RTM mit beiden Debug-Optionen vorgeschlagen hat, und sie hat sich gleich verhalten.

Die verkleinerte Version ist also nicht in der von mir erwarteten Reihenfolge, aber sie funktioniert.

jrummell
quelle

Antworten:

18

Dieses Verhalten tritt bei den RTM-Bits nicht auf. Verwenden Sie die Bits des Microsoft ASP.NET Web Optimization Framework 1.0.0: http://nuget.org/packages/Microsoft.AspNet.Web.Optimization ?

Ich habe einen ähnlichen Repro wie Ihr Beispiel verwendet, der auf einer neuen MVC4-Internetanwendungswebsite basiert.

Ich habe BundleConfig.RegisterBundles hinzugefügt:

        Bundle canvasScripts =
            new ScriptBundle("~/bundles/scripts/canvas")
                .Include("~/Scripts/modernizr-*")
                .Include("~/Scripts/Shared/achievements.js")
                .Include("~/Scripts/Shared/canvas.js");
        bundles.Add(canvasScripts); 

Und dann habe ich auf der Standardindexseite hinzugefügt:

<script src="@Scripts.Url("~/bundles/scripts/canvas")"></script>

Und ich habe überprüft, dass in dem minimierten Javascript für das Bundle der Inhalt von Errungenschaften.js nach der Modernisierung ...

Hao Kung
quelle
Ich habe auf die Version 1.0 aktualisiert. Siehe meine aktualisierte Frage. Der Inhalt von Errungenschaften.js ist zuerst in der minimierten Version enthalten, funktioniert jedoch, da es sich um eine später aufgerufene Funktion handelt.
Jrummell
114

Sie können einen benutzerdefinierten Bundle-Besteller ( IBundleOrderer) schreiben , der sicherstellt, dass Bundles in der Reihenfolge enthalten sind, in der Sie sie registrieren:

public class AsIsBundleOrderer : IBundleOrderer
{
    public virtual IEnumerable<FileInfo> OrderFiles(BundleContext context, IEnumerable<FileInfo> files)
    {
        return files;
    }
}

und dann:

public class BundleConfig
{
    public static void RegisterBundles(BundleCollection bundles)
    {
        var bundle = new Bundle("~/bundles/scripts/canvas");
        bundle.Orderer = new AsIsBundleOrderer();
        bundle
            .Include("~/Scripts/modernizr-*")
            .Include("~/Scripts/json2.js")
            .Include("~/Scripts/columnizer.js")
            .Include("~/Scripts/jquery.ui.message.min.js")
            .Include("~/Scripts/Shared/achievements.js")
            .Include("~/Scripts/Shared/canvas.js");
        bundles.Add(bundle);
    }
}

und aus Ihrer Sicht:

@Scripts.Render("~/bundles/scripts/canvas")
Darin Dimitrov
quelle
10
Dies sollte funktionieren, sollte aber auch unnötig sein, da der Standardbesteller im Allgemeinen die Reihenfolge der Includes einhält (es werden nur einige spezielle Dateien nach oben befördert, z. B. jquery, reset.css / normalize.css usw.). Es sollte nicht die Förderung von Errungenschaften sein.
Hao Kung
1
@flipdoubt Datei hier ein Problem mit dem Repro: aspnetoptimization.codeplex.com/workitem/list/advanced
Hao Kung
1
Das funktioniert perfekt, danke! Ich stimme jedoch zu, dies sollte nicht notwendig sein.
CBarr
2
Das funktioniert bei mir. Es hat jqueryui über Bootstrap beworben, als laut stackoverflow.com/questions/17367736/… ich es umgedreht haben muss.
Sethcran
1
@Hao Kung können wir die Dateien im Include- Verzeichnis bestellen ?
Shaijut
52

Danke Darin. Ich habe eine Erweiterungsmethode hinzugefügt.

internal class AsIsBundleOrderer : IBundleOrderer
{
    public virtual IEnumerable<FileInfo> OrderFiles(BundleContext context, IEnumerable<FileInfo> files)
    {
        return files;
    }
}

internal static class BundleExtensions
{
    public static Bundle ForceOrdered(this Bundle sb)
    {
        sb.Orderer = new AsIsBundleOrderer();
        return sb;
    }
}

Verwendung

bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
                    "~/Scripts/jquery-{version}.js",
                    "~/Scripts/jquery-migrate-{version}.js",

                    "~/Scripts/jquery.validate.js",
                    "~/Scripts/jquery.validate.messages_fr.js",
                    "~/Scripts/moon.jquery.validation-{version}.js",

                    "~/Scripts/jquery-ui-{version}.js"
                    ).ForceOrdered());
Softlion
quelle
9
Ziemlich schick! : D Gerade jetzt hatte ich Änderung FileInfofür BundleFilein der Methodensignatur - Schnittstelle. Sie haben es geändert.
Leniel Maccaferri
Ja, sie haben es in der Vorabversion geändert, die das Owin-Framework verwendet
Softlion
kleines Off-Topic: Hat jemand mehr Infos zu dieser BundleFileKlasse? Um eingebettete Ressourcen zu bündeln, versuche ich, dies zu instanziieren, und es erfordert einen (konkreten untergeordneten) VirtualFileParameter in seinem Konstruktor.
Superjos
Ich habe die gleiche Frage, ich muss BundleFile instanziieren, weiß aber nicht wie
Rui
2
@superjos Ich weiß, dass dies ziemlich alt ist, aber ich werde antworten, falls jemand anderes das Problem hat. Es ist Teil von System.Web.Optimization.
Talon
47

Die Antwort von SoftLion wurde aktualisiert, um Änderungen in MVC 5 (BundleFile vs FileInfo) zu verarbeiten.

internal class AsIsBundleOrderer : IBundleOrderer
{
    public virtual IEnumerable<BundleFile> OrderFiles(BundleContext context, IEnumerable<BundleFile> files)
    {
        return files;
    }
}

internal static class BundleExtensions
{
    public static Bundle ForceOrdered(this Bundle sb)
    {
        sb.Orderer = new AsIsBundleOrderer();
        return sb;
    }
}

Verwendung:

    bundles.Add(new ScriptBundle("~/content/js/site")
        .Include("~/content/scripts/jquery-{version}.js")
        .Include("~/content/scripts/bootstrap-{version}.js")
        .Include("~/content/scripts/jquery.validate-{version}")
        .ForceOrdered());

Ich verwende gerne fließende Syntax, aber es funktioniert auch mit einem einzigen Methodenaufruf und allen als Parameter übergebenen Skripten.

Gerald Davis
quelle
2
Funktioniert in MVC 5. Danke
Pascal Carmoni
4
Ausgezeichnet. Dies sollte von MS imho
Angelo
funktioniert auch in Web Forms wie ein Zauber ... im Grunde sollte es für alle neuesten Versionen gut funktionieren!
Sunny Sharma
Sehr saubere und einfache Lösung. Vielen Dank.
Bunjeeb
5

Sie sollten in der Lage sein, die Reihenfolge mithilfe der BundleCollection.FileSetOrderList festzulegen. Schauen Sie sich diesen Blog-Beitrag an: http://weblogs.asp.net/imranbaloch/archive/2012/09/30/hidden-options-of-asp-net-bundling-and-minification.aspx . Der Code in Ihrer Instanz wäre ungefähr so:

BundleFileSetOrdering bundleFileSetOrdering = new BundleFileSetOrdering("js");
bundleFileSetOrdering.Files.Add("~/Scripts/modernizr-*");
bundleFileSetOrdering.Files.Add("~/Scripts/json2.js");
bundleFileSetOrdering.Files.Add("~/Scripts/columnizer.js");
bundleFileSetOrdering.Files.Add("~/Scripts/jquery.ui.message.min.js");
bundleFileSetOrdering.Files.Add("~/Scripts/Shared/achievements.js");
bundleFileSetOrdering.Files.Add("~/Scripts/Shared/canvas.js");
bundles.FileSetOrderList.Add(bundleFileSetOrdering);
Arne H. Bitubekk
quelle
1

Sie sollten die Verwendung der Kassette http://getcassette.net/ in Betracht ziehen. Sie unterstützt die automatische Erkennung von Skriptabhängigkeiten basierend auf Skriptreferenzen, die in jeder Datei enthalten sind.

Wenn Sie sich lieber an die MS Web Optimization-Lösung halten, aber die Idee mögen, Skripte basierend auf Skriptreferenzen anzuordnen, sollten Sie meinen Blog-Beitrag unter http://blogs.microsoft.co.il/oric/2013/12/27/building-single lesen -page-application-bundle-orderer /

Ori Calvo
quelle
1

Die Antwort von @Darin Dimitrov funktioniert perfekt für mich, aber mein Projekt ist in VB geschrieben. Hier ist seine Antwort, die in VB konvertiert wurde

Public Class AsIsBundleOrderer
    Implements IBundleOrderer

    Public Function IBundleOrderer_OrderFiles(ByVal context As BundleContext, ByVal files As IEnumerable(Of FileInfo)) As IEnumerable(Of FileInfo) Implements IBundleOrderer.OrderFiles
        Return files
    End Function
End Class

Um es zu benutzen:

Dim scriptBundleMain = New ScriptBundle("~/Scripts/myBundle")
scriptBundleMain.Orderer = New AsIsBundleOrderer()
scriptBundleMain.Include(
    "~/Scripts/file1.js",
    "~/Scripts/file2.js"
)
bundles.Add(scriptBundleMain)
CBarr
quelle
Ich bekomme immer wieder den Fehler: Class 'AsIsBundleOrderer' must implement 'Function OrderFiles(context as ....)' for interface 'System.Web.Optimization.IBundleOrderer'Irgendwelche Ideen?
Mark Pieszak - Trilon.io