Comment puis-je spécifier un ordre d'inclusion ScriptBundle explicite?

91

J'essaye la fonctionnalité MVC4 System.Web.Optimization 1.0 ScriptBundle .

J'ai la configuration suivante:

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

et la vue suivante:

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

BundlePaths.CanvasScriptsest "~/bundles/scripts/canvas". Il rend ceci:

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

Jusqu'ici tout va bien, sauf ~/Scripts/Shared/achievements.jsle premier script de la source fournie. Cela dépend de chaque script inclus avant dans le fichier ScriptBundle. Comment puis-je m'assurer qu'il respecte l'ordre dans lequel j'ajoute des instructions d'inclusion à l'ensemble?

Mettre à jour

Il s'agissait d'une application ASP.NET MVC 4 relativement nouvelle, mais elle faisait référence au package de pré-version du framework d'optimisation. Je l'ai supprimé et ajouté le package RTM de http://nuget.org/packages/Microsoft.AspNet.Web.Optimization . Avec la version RTM avec debug = true dans web.config, @Scripts.Render("~/bundles/scripts/canvas")restitue les balises de script individuelles dans le bon ordre.

Avec debug = false dans web.config, le script combiné a d'abord le script réalisations.js, mais comme il s'agit d'une définition de fonction (constructeur d'objet) appelée plus tard, il s'exécute sans erreur. Peut-être que le minificateur est assez intelligent pour comprendre les dépendances?

J'ai également essayé l' IBundleOrdererimplémentation suggérée par Darin Dimitrov avec RTM avec les deux options de débogage et elle s'est comportée de la même manière.

La version minifiée n'est donc pas dans l'ordre que j'attends, mais cela fonctionne.

jrummell
la source

Réponses:

18

Je ne vois pas ce comportement sur les bits RTM, utilisez-vous les bits Microsoft ASP.NET Web Optimization Framework 1.0.0: http://nuget.org/packages/Microsoft.AspNet.Web.Optimization ?

J'ai utilisé une reproduction similaire à votre échantillon, basée sur un nouveau site Web d'application Internet MVC4.

J'ai ajouté à BundleConfig.RegisterBundles:

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

Et puis dans la page d'index par défaut, j'ai ajouté:

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

Et j'ai vérifié que dans le javascript minifié du bundle, le contenu de achievement.js était après modernizr ...

Hao Kung
la source
Je suis passé à la version 1.0. Voir ma question mise à jour. Le contenu de achievement.js est inclus en premier dans la version minifiée, mais cela fonctionne car c'est une fonction appelée plus tard.
jrummell
115

Vous pouvez écrire un outil de commande de IBundleOrdererlots personnalisé ( ) qui garantira que les lots sont inclus dans l'ordre dans lequel vous les enregistrez:

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

puis:

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

et à votre avis:

@Scripts.Render("~/bundles/scripts/canvas")
Darin Dimitrov
la source
10
Cela devrait fonctionner, mais cela devrait également être inutile, car le classeur par défaut respecte généralement l'ordre des includes (il ne promeut que quelques fichiers spéciaux vers le haut, c'est-à-dire jquery, reset.css / normalize.css, etc.). Il ne devrait pas promouvoir les réalisations.js au sommet.
Hao Kung
1
@flipdoubt file un problème ici avec la repro: aspnetoptimization.codeplex.com/workitem/list/advanced
Hao Kung
1
Cela fonctionne parfaitement, merci! Je suis d’accord cependant que cela ne devrait pas être nécessaire.
CBarr
2
Cela fonctionne pour moi. Il faisait la promotion de jqueryui sur bootstrap quand, selon stackoverflow.com/questions/17367736/ ... j'ai besoin de le retourner.
Sethcran
1
@Hao Kung pouvons-nous commander les fichiers dans le répertoire d' inclusion ?
shaijut
52

Merci Darin. J'ai ajouté une méthode d'extension.

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

Usage

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
la source
9
Assez lisse! : D En ce moment, je devais changer FileInfopour BundleFilela signature de la méthode d'interface. Ils l'ont changé.
Leniel Maccaferri
oui, ils l'ont changé dans la version préliminaire qui utilise le framework
Owin
little Off-Topic: est-ce que quelqu'un a plus d'informations sur ce BundleFilecours? Afin de regrouper les ressources intégrées, j'essaie de l'instancier et cela nécessite un VirtualFileparamètre (enfant concret de) dans son constructeur.
superjos
J'ai la même question, j'ai besoin d'instancier BundleFile, mais je ne sais pas comment
Rui
2
@superjos Je sais que c'est assez ancien mais je répondrai au cas où quelqu'un d'autre aurait le problème. Cela fait partie de System.Web.Optimization.
Talon
47

Mise à jour de la réponse fournie par SoftLion pour gérer les modifications dans MVC 5 (BundleFile vs FileInfo).

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

Usage:

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

J'aime utiliser une syntaxe fluide, mais cela fonctionne également avec un seul appel de méthode et tous les scripts passés en paramètres.

Gerald Davis
la source
2
Fonctionne dans MVC 5. Merci
Pascal Carmoni
4
Excellent. Cela devrait être inclus par MS imho
Angelo
fonctionne comme un charme dans Web Forms aussi ... en gros, cela devrait fonctionner correctement pour toutes les dernières versions!
Sunny Sharma
Solution très propre et simple. Merci beaucoup.
bunjeeb
5

Vous devriez être en mesure de définir l'ordre à l'aide de BundleCollection.FileSetOrderList. Jetez un œil à ce billet de blog: http://weblogs.asp.net/imranbaloch/archive/2012/09/30/hidden-options-of-asp-net-bundling-and-minification.aspx . Le code de votre instance serait quelque chose comme:

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
la source
1

Vous devriez envisager d'utiliser Cassette http://getcassette.net/ Il prend en charge la détection automatique des dépendances de script en fonction des références de script trouvées dans chaque fichier.

Si vous préférez vous en tenir à la solution MS Web Optimization mais que vous aimez l'idée d'organiser des scripts basés sur des références de script, vous devriez lire mon article de blog à l' adresse http://blogs.microsoft.co.il/oric/2013/12/27/building-single -page-application-bundle-commande /

Ori Calvo
la source
1

La réponse de @Darin Dimitrov fonctionne parfaitement pour moi, mais mon projet est écrit en VB, alors voici sa réponse convertie en VB

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

Pour l'utiliser:

Dim scriptBundleMain = New ScriptBundle("~/Scripts/myBundle")
scriptBundleMain.Orderer = New AsIsBundleOrderer()
scriptBundleMain.Include(
    "~/Scripts/file1.js",
    "~/Scripts/file2.js"
)
bundles.Add(scriptBundleMain)
CBarr
la source
Je reçois toujours cette erreur: des Class 'AsIsBundleOrderer' must implement 'Function OrderFiles(context as ....)' for interface 'System.Web.Optimization.IBundleOrderer'idées?
Mark Pieszak - Trilon.io