Bagaimana cara menentukan perintah penyertaan ScriptBundle eksplisit?

91

Saya mencoba fitur MVC4 System.Web.Optimization 1.0 ScriptBundle .

Saya memiliki konfigurasi berikut:

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

dan tampilan berikut:

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

di mana BundlePaths.CanvasScriptsadalah "~/bundles/scripts/canvas". Ini membuat ini:

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

Sejauh ini bagus, kecuali ~/Scripts/Shared/achievements.jsadalah skrip pertama dalam sumber yang dibundel. Itu tergantung pada setiap skrip yang disertakan sebelumnya di ScriptBundle. Bagaimana saya bisa memastikan bahwa itu menghormati urutan di mana saya menambahkan pernyataan include ke bundel?

Memperbarui

Ini adalah aplikasi ASP.NET MVC 4 yang relatif baru, tetapi merujuk pada paket pra rilis kerangka kerja pengoptimalan. Saya menghapusnya dan menambahkan paket RTM dari http://nuget.org/packages/Microsoft.AspNet.Web.Optimization . Dengan versi RTM dengan debug = true di web.config, @Scripts.Render("~/bundles/scripts/canvas")merender tag skrip individual dalam urutan yang benar.

Dengan debug = false di web.config, skrip gabungan memiliki skrip achievement.js terlebih dahulu, tetapi karena itu adalah definisi fungsi (konstruktor objek) yang dipanggil kemudian, skrip ini berjalan tanpa kesalahan. Mungkin penambang cukup pintar untuk mengetahui dependensi?

Saya juga mencoba IBundleOrdererimplementasi yang disarankan Darin Dimitrov dengan RTM dengan kedua opsi debug dan berperilaku sama.

Jadi, versi yang diperkecil tidak dalam urutan yang saya harapkan, tetapi berfungsi.

jrummell
sumber

Jawaban:

18

Saya tidak melihat perilaku ini pada bit RTM, apakah Anda menggunakan bit Microsoft ASP.NET Web Optimization Framework 1.0.0: http://nuget.org/packages/Microsoft.AspNet.Web.Optimization ?

Saya menggunakan repro serupa dengan sampel Anda, berdasarkan situs web aplikasi Internet MVC4 baru.

Saya menambahkan ke 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); 

Dan kemudian di halaman indeks default, saya menambahkan:

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

Dan saya memverifikasi bahwa dalam javascript yang diperkecil untuk bundel, konten achievement.js setelah modernizr ...

Hao Kung
sumber
Saya meningkatkan ke versi 1.0. Lihat pertanyaan saya yang diperbarui. Konten achievement.js disertakan pertama kali dalam versi yang diperkecil, tetapi berfungsi karena fungsinya dipanggil nanti.
jrummell
115

Anda bisa menulis pemesan bundel khusus ( IBundleOrderer) yang akan memastikan bundel disertakan dalam urutan Anda mendaftarkannya:

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

lalu:

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

dan menurut pandangan Anda:

@Scripts.Render("~/bundles/scripts/canvas")
Darin Dimitrov
sumber
10
Ini semestinya berfungsi, tetapi juga tidak perlu, karena pengorder default umumnya menghormati urutan penyertaan (ini hanya mempromosikan beberapa file khusus ke atas, yaitu jquery, reset.css / normalize.css, dll.). Itu seharusnya tidak mempromosikan achievement.js ke atas.
Hao Kung
1
@flipdoubt mengajukan masalah di sini dengan repro: aspnetoptimization.codeplex.com/workitem/list/advanced
Hao Kung
1
Ini bekerja dengan sempurna, terima kasih! Saya setuju, ini seharusnya tidak perlu.
CBarr
2
Ini berhasil untuk saya. Itu mempromosikan jqueryui melalui bootstrap ketika menurut stackoverflow.com/questions/17367736/… Saya membutuhkannya dibalik.
Sethcran
1
@Hao Kung bisakah kita memesan file di direktori include ?
shaijut
52

Terima kasih Darin. Saya telah menambahkan metode ekstensi.

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

Pemakaian

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
sumber
9
Cukup apik! : D Sekarang saya harus perubahan FileInfountuk BundleFiledalam metode tanda tangan Interface. Mereka mengubahnya.
Leniel Maccaferri
ya, mereka mengubahnya dalam versi
prarilis
sedikit Off-Topic: apakah ada yang punya info lebih lanjut tentang BundleFilekelas ini ? Untuk menggabungkan sumber daya yang disematkan, saya mencoba untuk membuat instance itu dan itu membutuhkan VirtualFileparameter (anak konkret) dalam konstruktornya.
superjos
Saya memiliki pertanyaan yang sama, saya perlu membuat instantiate BundleFile, tetapi tidak tahu caranya
Rui
2
@superjos Saya tahu ini agak tua tetapi saya akan menjawab jika ada orang lain yang memiliki masalah. Itu bagian dari System.Web.Optimization.
Talon
47

Memperbarui jawaban yang disediakan oleh SoftLion untuk menangani perubahan dalam 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;
    }
}

Pemakaian:

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

Saya suka menggunakan sintaks yang lancar tetapi juga berfungsi dengan panggilan metode tunggal dan semua skrip diteruskan sebagai parameter.

Gerald Davis
sumber
2
Bekerja di MVC 5. Terima kasih
Pascal Carmoni
4
Luar biasa. Ini harus dimasukkan oleh MS imho
Angelo
berfungsi seperti pesona di Formulir Web juga ... pada dasarnya ini harus berfungsi dengan baik untuk semua versi terbaru!
Cerah Sharma
Solusi yang sangat bersih dan sederhana. Terima kasih banyak.
bunjeeb
5

Anda harus dapat mengatur pesanan dengan bantuan BundleCollection.FileSetOrderList. Lihat postingan blog ini: http://weblogs.asp.net/imranbaloch/archive/2012/09/30/hidden-options-of-asp-net-bundling-and-minification.aspx . Kode dalam instance Anda akan menjadi seperti ini:

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
sumber
1

Anda harus mempertimbangkan untuk menggunakan Cassette http://getcassette.net/ Ini mendukung deteksi otomatis ketergantungan skrip berdasarkan referensi skrip yang ditemukan di dalam setiap file.

Jika Anda lebih suka tetap berpegang pada solusi Pengoptimalan Web MS tetapi menyukai gagasan mengatur skrip berdasarkan referensi skrip, Anda harus membaca posting blog saya di http://blogs.microsoft.co.il/oric/2013/12/27/building-single -page-application-bundle-orderer /

Ori Calvo
sumber
1

Jawaban @Darin Dimitrov bekerja dengan sempurna untuk saya, tetapi proyek saya ditulis dalam VB, jadi inilah jawabannya diubah menjadi 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

Untuk menggunakannya:

Dim scriptBundleMain = New ScriptBundle("~/Scripts/myBundle")
scriptBundleMain.Orderer = New AsIsBundleOrderer()
scriptBundleMain.Include(
    "~/Scripts/file1.js",
    "~/Scripts/file2.js"
)
bundles.Add(scriptBundleMain)
CBarr
sumber
Saya terus mendapatkan kesalahan ini: Class 'AsIsBundleOrderer' must implement 'Function OrderFiles(context as ....)' for interface 'System.Web.Optimization.IBundleOrderer' Ada ide?
Mark Pieszak - Trilon.io