Как я могу указать явный порядок включения ScriptBundle?

Я пробую функцию MVC4 System.Web.Optimization 1.0 ScriptBundle.

У меня есть следующая конфигурация:

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

и следующий вид:

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

где BundlePaths.CanvasScripts является "~/bundles/scripts/canvas", Это делает это:

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

Пока все хорошо, кроме ~/Scripts/Shared/achievements.js это первый скрипт в комплекте источника. Это зависит от каждого сценария, включенного перед ScriptBundle, Как я могу гарантировать, что он соблюдает порядок, в котором я добавляю операторы include в пакет?

Обновить

Это было относительно новое приложение ASP.NET MVC 4, но оно ссылалось на пакет предварительной версии инфраструктуры оптимизации. Я удалил его и добавил пакет RTM из http://nuget.org/packages/Microsoft.AspNet.Web.Optimization. С версией RTM с debug=true в web.config, @Scripts.Render("~/bundles/scripts/canvas") отображает отдельные теги сценариев в правильном порядке.

С debug=false в web.config объединенный сценарий сначала имеет сценарий достижений.js, но, поскольку это определение функции (конструктор объекта), который вызывается позже, он выполняется без ошибок. Возможно, минификатор достаточно умен, чтобы выяснить зависимости?

Я также попробовал IBundleOrderer реализация, которую Дарин Димитров предложил с RTM с обоими вариантами отладки, и он вел себя одинаково.

Так что уменьшенная версия не в том порядке, который я ожидаю, но она работает.

7 ответов

Решение

Я не вижу такого поведения в битах RTM. Используете ли вы биты Microsoft ASP.NET Web Optimization Framework 1.0.0: http://nuget.org/packages/Microsoft.AspNet.Web.Optimization?

Я использовал аналогичную копию вашего примера, основанную на новом веб-сайте MVC4.

Я добавил в 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); 

А затем на странице индекса по умолчанию я добавил:

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

И я проверил, что в минимизированном javascript для комплекта содержимое достижений.js было после модернизатора...

Вы можете написать заказной комплект поставки (IBundleOrderer), который обеспечит включение пакетов в порядок их регистрации:

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

а потом:

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

и по вашему мнению:

@Scripts.Render("~/bundles/scripts/canvas")

Спасибо, Дарин. Я добавил метод расширения.

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

использование

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, для обработки изменений в 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;
    }
}

Использование:

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

Мне нравится использовать свободный синтаксис, но он также работает с одним вызовом метода, и все сценарии передаются в качестве параметров.

Вы должны быть в состоянии установить порядок с помощью BundleCollection.FileSetOrderList. Посмотрите этот пост в блоге: http://weblogs.asp.net/imranbaloch/archive/2012/09/30/hidden-options-of-asp-net-bundling-and-minification.aspx. Код в вашем экземпляре будет выглядеть примерно так:

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

Ответ @Darin Dimitrov отлично работает для меня, но мой проект написан на VB, так что вот его ответ, преобразованный в 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

Чтобы использовать это:

Dim scriptBundleMain = New ScriptBundle("~/Scripts/myBundle")
scriptBundleMain.Orderer = New AsIsBundleOrderer()
scriptBundleMain.Include(
    "~/Scripts/file1.js",
    "~/Scripts/file2.js"
)
bundles.Add(scriptBundleMain)

Вы должны рассмотреть возможность использования кассеты http://getcassette.net/ Она поддерживает автоматическое обнаружение зависимостей скриптов на основе ссылок скриптов, найденных внутри каждого файла.

Если вы предпочитаете придерживаться решения MS Web Optimization, но вам нравится идея размещения сценариев на основе ссылок на сценарии, вы должны прочитать мой пост в блоге по адресу http://blogs.microsoft.co.il/oric/2013/12/27/building-single-page-application-bundle-orderer/

Другие вопросы по тегам