MVC4 StyleBundle не отображает пакет в правильном порядке


Я пытаюсь отрисовать связку css-файлов, но вывод получается в неправильном порядке. Я попробовал решение @ MVC4 Beta Minification and Bundling: упорядочивание файлов и отладка в браузере , но это не помогло. Вот связка:

bundles.Add(new StyleBundle("~/stylesheet")
    .Include("~/css/main.css")
    .Include("~/css/mvc.css")
    .Include("~/js/jquery.thickbox.css")
    .Include("~/js/jquery.rating.css")
    .Include("~/css/ProductListing.css")
    .Include("~/css/dropdown/dropdown.css")
    .Include("~/css/dropdown/dropdown.vertical.css")
    .Include("~/js/fancybox/jquery.fancybox-1.3.1.css")
    .Include("~/css/scartpopup.css")
    .Include("~/css/ShoppingCart.css")
    .Include("~/css/ceebox.css")
    .Include("~/css/tooltip.css")
    .Include("~/css/recent_blog_posts.css")
    .Include("~/css/ProductDetail.css")
    .Include("~/css/jquery-ui-1.7.3.custom.css")
    .Include("~/css/filter_box.css")
    .Include("~/css/custom_page.css")
    .Include("~/css/Checkout.css")
    .Include("~/css/CheckoutButton.css")
);

И вот результат, как вы можете видеть, jQuery-ui выходит на первое место.

<link href="/css/jquery-ui-1.7.3.custom.css" rel="stylesheet"/>
<link href="/css/main.css" rel="stylesheet"/>
<link href="/css/mvc.css" rel="stylesheet"/>
<link href="/js/jquery.thickbox.css" rel="stylesheet"/>
<link href="/js/jquery.rating.css" rel="stylesheet"/>
<link href="/css/ProductListing.css" rel="stylesheet"/>
<link href="/css/dropdown/dropdown.css" rel="stylesheet"/>
<link href="/css/dropdown/dropdown.vertical.css" rel="stylesheet"/>
<link href="/js/fancybox/jquery.fancybox-1.3.1.css" rel="stylesheet"/>
<link href="/css/scartpopup.css" rel="stylesheet"/>
<link href="/css/ShoppingCart.css" rel="stylesheet"/>
<link href="/css/ceebox.css" rel="stylesheet"/>
<link href="/css/tooltip.css" rel="stylesheet"/>
<link href="/css/recent_blog_posts.css" rel="stylesheet"/>
<link href="/css/ProductDetail.css" rel="stylesheet"/>
<link href="/css/filter_box.css" rel="stylesheet"/>
<link href="/css/custom_page.css" rel="stylesheet"/>
<link href="/css/Checkout.css" rel="stylesheet"/>
<link href="/css/CheckoutButton.css" rel="stylesheet"/>

Как я могу убедиться, что таблицы стилей отображаются в правильном порядке?

1 14

1 ответ:

Связывание не должно отображать CSS-файлы в одном и том же порядке, оно следует другой логике. Если вам нужно отрисовать их как определено, то вы должны создать пользовательский IBundleOrderer и установить его в пакет как требуемый Orderer:

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

И

var bundle = new StyleBundle("~/stylesheet");
bundle.Orderer = new AsDefinedBundleOrderer();
bundles.Add(bundle);

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

Обновление порядка по умолчанию

Связывание использует понятие IBundleOrderer для сортировки элементов в пределах Bundle. Класс Bundle обладает свойством Orderer, которое выглядит следующим образом:

public IBundleOrderer Orderer
{
  get
  {
    if (this._orderer == null)
      return (IBundleOrderer) DefaultBundleOrderer.Instance;
    else
      return this._orderer;
  }
  set
  {
    this._orderer = value;
    this.InvalidateCacheEntries();
  }
}

Таким образом, по умолчанию orderer на самом деле является DefaultBundleOrderer, пока вы не перепишете его с помощью пользовательского orderer.

У IBundleOrderer есть следующая подпись:

public interface IBundleOrderer
{
  IEnumerable<FileInfo> OrderFiles(BundleContext context, IEnumerable<FileInfo> files);
}

Реализация DefaultBundleOrderer этого упорядочивает файлы по BundleContext, Вот фрагмент из реализации OrderFiles:

  foreach (BundleFileSetOrdering ordering in (IEnumerable<BundleFileSetOrdering>) context.BundleCollection.FileSetOrderList)
    DefaultBundleOrderer.AddOrderingFiles(ordering, (IEnumerable<FileInfo>) list, fileMap, foundFiles, result);

Так что другой результат происходит потому, что это. Это, конечно, не случайность алгоритм сортировки :) Правила определены в классе BUndleCollection:

public static void AddDefaultFileOrderings(IList<BundleFileSetOrdering> list)
{
  if (list == null)
    throw new ArgumentNullException("list");
  BundleFileSetOrdering bundleFileSetOrdering1 = new BundleFileSetOrdering("css");
  bundleFileSetOrdering1.Files.Add("reset.css");
  bundleFileSetOrdering1.Files.Add("normalize.css");
  list.Add(bundleFileSetOrdering1);
  BundleFileSetOrdering bundleFileSetOrdering2 = new BundleFileSetOrdering("jquery");
  bundleFileSetOrdering2.Files.Add("jquery.js");
  bundleFileSetOrdering2.Files.Add("jquery-min.js");
  bundleFileSetOrdering2.Files.Add("jquery-*");
  bundleFileSetOrdering2.Files.Add("jquery-ui*");
  bundleFileSetOrdering2.Files.Add("jquery.ui*");
  bundleFileSetOrdering2.Files.Add("jquery.unobtrusive*");
  bundleFileSetOrdering2.Files.Add("jquery.validate*");
  list.Add(bundleFileSetOrdering2);
  BundleFileSetOrdering bundleFileSetOrdering3 = new BundleFileSetOrdering("modernizr");
  bundleFileSetOrdering3.Files.Add("modernizr-*");
  list.Add(bundleFileSetOrdering3);
  BundleFileSetOrdering bundleFileSetOrdering4 = new BundleFileSetOrdering("dojo");
  bundleFileSetOrdering4.Files.Add("dojo.*");
  list.Add(bundleFileSetOrdering4);
  BundleFileSetOrdering bundleFileSetOrdering5 = new BundleFileSetOrdering("moo");
  bundleFileSetOrdering5.Files.Add("mootools-core*");
  bundleFileSetOrdering5.Files.Add("mootools-*");
  list.Add(bundleFileSetOrdering5);
  BundleFileSetOrdering bundleFileSetOrdering6 = new BundleFileSetOrdering("prototype");
  bundleFileSetOrdering6.Files.Add("prototype.js");
  bundleFileSetOrdering6.Files.Add("prototype-*");
  bundleFileSetOrdering6.Files.Add("scriptaculous-*");
  list.Add(bundleFileSetOrdering6);
  BundleFileSetOrdering bundleFileSetOrdering7 = new BundleFileSetOrdering("ext");
  bundleFileSetOrdering7.Files.Add("ext.js");
  bundleFileSetOrdering7.Files.Add("ext-*");
  list.Add(bundleFileSetOrdering7);
}

Поэтому, когда вы вызываете это из Application_Start:

BundleConfig.RegisterBundles(BundleTable.Bundles);

Фактически вы передаете значение по умолчанию BundleCollection, определенное в библиотеке.

Таким образом, мы имеем BundleFileSetOrdering экземпляры, передаваемые один за другим в:

private static void AddOrderingFiles(BundleFileSetOrdering ordering, IEnumerable<FileInfo> files, Dictionary<string, HashSet<FileInfo>> fileMap, HashSet<FileInfo> foundFiles, List<FileInfo> result)
{
  foreach (string key in (IEnumerable<string>) ordering.Files)
  {
    if (key.EndsWith("*", StringComparison.OrdinalIgnoreCase))
    {
      string str = key.Substring(0, key.Length - 1);
      foreach (FileInfo fileInfo in files)
      {
        if (!foundFiles.Contains(fileInfo) && fileInfo.Name.StartsWith(str, StringComparison.OrdinalIgnoreCase))
        {
          result.Add(fileInfo);
          foundFiles.Add(fileInfo);
        }
      }
    }
    else if (fileMap.ContainsKey(key))
    {
      List<FileInfo> list = new List<FileInfo>((IEnumerable<FileInfo>) fileMap[key]);
      list.Sort((IComparer<FileInfo>) FileInfoComparer.Instance);
      foreach (FileInfo fileInfo in list)
      {
        if (!foundFiles.Contains(fileInfo))
        {
          result.Add(fileInfo);
          foundFiles.Add(fileInfo);
        }
      }
    }
  }
}

Заключение

Если мы хотим упростить процесс, то можем сказать, что библиотека предпочитает какие-то файлы и делает некоторую сортировку по другим файлам, если найдено несколько возможностей. Это ожидаемое поведение большую часть времени, но, как вы можете видеть, оно легко переопределяется с помощью AsDefinedBundleOrderer, поэтому он ничего не делает с данным набором файлов, поэтому порядок остается первоначальным.