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

91

Я пробую функцию 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, объединенный скрипт сначала содержит скрипт achievement.js, но, поскольку это определение функции (конструктор объекта), вызываемое позже, он выполняется без ошибок. Возможно, минификатор достаточно умен, чтобы вычислить зависимости?

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

Таким образом, уменьшенная версия находится не в том порядке, в котором я ожидал, но она работает.

Jrummell
источник

Ответы:

18

Я не вижу такого поведения в битах 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 для пакета содержимое achievement.js было после modernizr ...

Хао Кунг
источник
Перешел на версию 1.0. См. Мой обновленный вопрос. В минифицированную версию сначала включается содержимое achievement.js, но он работает, поскольку его функция вызывается позже.
jrummell
115

Вы можете написать собственный пакет orderer ( 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")
Дарин Димитров
источник
10
Это должно сработать, но в этом также не должно быть необходимости, поскольку заказчик по умолчанию обычно соблюдает порядок включения (он продвигает наверх только несколько специальных файлов, например jquery, reset.css / normalize.css и т. Д.). Он не должен продвигать достижения .js в топ.
Hao Kung
1
@flipdoubt файл здесь проблема с воспроизведением
Хао Кунг
1
Это отлично работает, спасибо! Но я согласен, в этом нет необходимости.
CBarr
2
У меня это работает. Он продвигал jqueryui поверх начальной загрузки, когда, согласно stackoverflow.com/questions/17367736/ ... Мне нужно, чтобы он перевернулся.
Сеткран
1
@Hao кунг мы можем заказать файлы в включают каталог?
Shaijut
52

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

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
источник
9
Довольно ловко! : D Прямо сейчас я должен был изменить FileInfoдля BundleFileв сигнатуре метода интерфейса. Они изменили это.
Leniel Maccaferri 04
да, они изменили его в предварительной версии, которая использует фреймворк
Owin
немного вне темы: есть ли у кого-нибудь больше информации об этом BundleFileклассе? Чтобы связать встроенные ресурсы, я пытаюсь создать его экземпляр, и для этого требуется параметр (конкретный дочерний элемент) VirtualFileв его конструкторе.
superjos
У меня тот же вопрос, мне нужно создать экземпляр BundleFile, но я не знаю, как
Руи
2
@superjos Я знаю, что это довольно давно, но я отвечу, если у кого-то еще возникнет проблема. Это часть System.Web.Optimization.
Talon
47

Обновлен ответ, предоставленный 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());

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

Джеральд Дэвис
источник
2
Работает в MVC 5. Спасибо
Паскаль Кармони
4
Превосходно. Это должно быть включено MS imho
Анджело
работает как шарм и в веб-формах ... в основном он должен работать нормально для всех последних версий!
Санни Шарма
Очень чистое и простое решение. Большое спасибо.
bunjeeb
5

У вас должна быть возможность установить порядок с помощью 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);
Арне Х. Битубекк
источник
1

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

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

Ори Кальво
источник
1

Ответ @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)
CBarr
источник
Я все время получаю эту ошибку: есть Class 'AsIsBundleOrderer' must implement 'Function OrderFiles(context as ....)' for interface 'System.Web.Optimization.IBundleOrderer'идеи?
Марк Пешак - Trilon.io