Объединение и минификация

Рик Андерсон

Объединение и минификация — это два метода, которые можно использовать в ASP.NET 4.5 для увеличения времени загрузки запроса. Объединение и минификация сокращают время загрузки за счет сокращения количества запросов к серверу и уменьшения размера запрошенных ресурсов (таких как CSS и JavaScript).

Большинство текущих основных браузеров ограничивают количество одновременных подключений на каждое имя узла шестью. Это означает, что пока обрабатываются шесть запросов, дополнительные запросы на ресурсы на узле будут помещены в очередь браузером. На приведенном ниже рисунке на сетевых вкладках средств разработчика IE F12 отображается время для ресурсов, необходимых для представления О программе примера приложения.

Б/М

На серых полосах отображается время постановки запроса в очередь браузером, ожидая шесть подключений. Желтая полоса — это время запроса до первого байта, то есть время, затраченное на отправку запроса и получение первого ответа от сервера. Синие полосы показывают время, затраченного на получение данных ответа от сервера. Вы можете дважды щелкнуть ресурс, чтобы получить подробные сведения о времени. Например, на следующем рисунке показаны сведения о времени загрузки файла /Scripts/MyScripts/JavaScript6.js .

Снимок экрана, на котором показана сетевая вкладка средств разработчика AS P NET с URL-адресами запросов на ресурсы в левом столбце и временем их выполнения в правом столбце.

На приведенном выше изображении показано событие Start , которое указывает время постановки запроса в очередь, так как браузер ограничивает количество одновременных подключений. В этом случае запрос помещался в очередь на 46 миллисекундах, ожидая завершения другого запроса.

Объединение

Объединение — это новая функция в ASP.NET 4.5, которая упрощает объединение или объединение нескольких файлов в один файл. Вы можете создавать CSS, JavaScript и другие пакеты. Чем меньше файлов, тем меньше HTTP-запросов, что может повысить производительность загрузки первой страницы.

На следующем рисунке показано то же представление времени представления О программе, показанное ранее, но на этот раз с включенным объединением и минификации.

Снимок экрана: вкладка сведений о времени ресурса в средствах разработчика I E F 12. Выделено событие Start.

Минификация

Минификация выполняет различные оптимизации кода для скриптов или css, например удаляет ненужные пробелы и комментарии и сокращает имена переменных до одного символа. Рассмотрим следующую функцию JavaScript.

AddAltToImg = function (imageTagAndImageID, imageContext) {
    ///<signature>
    ///<summary> Adds an alt tab to the image
    // </summary>
    //<param name="imgElement" type="String">The image selector.</param>
    //<param name="ContextForImage" type="String">The image context.</param>
    ///</signature>
    var imageElement = $(imageTagAndImageID, imageContext);
    imageElement.attr('alt', imageElement.attr('id').replace(/ID/, ''));
}

После минификации функция сводится к следующему:

AddAltToImg = function (n, t) { var i = $(n, t); i.attr("alt", i.attr("id").replace(/ID/, "")) }

Помимо удаления примечаний и ненужных пробелов, следующие параметры и имена переменных были переименованы (сокращены) следующим образом:

Исходное значение Renamed
imageTagAndImageID n
imageContext t
imageElement i

Влияние объединения и минификации

В следующей таблице показано несколько важных различий между перечислением всех ресурсов по отдельности и использованием объединения и минификации (B/M) в примере программы.

Использование B/M Без О/М Изменение
Запросы файлов 9 34 256%
Отправлено базы знаний 3.26 11.92 266%
Получено базы знаний 388.51 530 36 %
Время загрузки 510 МС 780 МС 53%

Отправленные байты значительно сократились с объединением, так как браузеры довольно подробны с заголовками HTTP, которые они применяют к запросам. Сокращение полученных байтов не так велико, так как самые большие файлы (Scripts\jquery-ui-1.8.11.min.js и Scripts\jquery-1.7.1.min.js) уже минифицированы. Примечание. Для имитации медленной сети в примере программы использовалось средство Fiddler . (В меню Fiddler Rules (Правила Fiddler) выберите Performance (Производительность ), а затем — Simulate Modem Speeds (Смоделировать скорость модема).)

Отладка пакетного и minified JavaScript

Легко выполнить отладку JavaScript в среде разработки (где элемент компиляции в файле Web.config имеет значение debug="true" ), так как файлы JavaScript не упаковываются и не миниифицируются. Вы также можете отлаживать сборку выпуска, в которой файлы JavaScript упаковываются и минимифицированы. С помощью средств разработчика IE F12 можно отлаживать функцию JavaScript, включенную в миниифицированный пакет, используя следующий подход:

  1. Перейдите на вкладку Скрипт и нажмите кнопку Начать отладку .
  2. Выберите пакет, содержащий функцию JavaScript, которую вы хотите отладить, с помощью кнопки активы.
    Снимок экрана, на котором показана вкладка скрипта средства разработчика I E F 12. Выделены поле ввода скрипта поиска, пакет и функция скрипта Java.
  3. Отформатируйте мини-код JavaScript, нажав кнопку КонфигурацияИзображение, отображающее значок кнопки Конфигурация, а затем выбрав Формат JavaScript.
  4. В поле Входные данные Скрипт поиска выберите имя функции, которую требуется отладить. На следующем рисунке в поле входных данных Search Script введено значение AddAltToImg.
    Снимок экрана, на котором показана вкладка скрипта средства разработчика I E F 12. Выделено поле ввода скрипта поиска с введенным параметром Add Alt To lmg .

Дополнительные сведения об отладке с помощью средств разработчика F12 см. в статье MSDN Использование средств разработчика F12 для отладки ошибок JavaScript.

Управление объединением и минификации

Объединение и минификация включаются или отключается путем установки значения атрибута debug в элементе компиляции в файлеWeb.config . В следующем XML-коде имеет значение true, debug поэтому объединение и минификация отключены.

<system.web>
    <compilation debug="true" />
    <!-- Lines removed for clarity. -->
</system.web>

Чтобы включить объединение и минификации, присвойте debug значение false. Параметр Web.config можно переопределить свойством EnableOptimizations класса BundleTable . Следующий код включает объединение и минификацию, а также переопределяет любые параметры в файлеWeb.config .

public static void RegisterBundles(BundleCollection bundles)
{
    bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
                 "~/Scripts/jquery-{version}.js"));

    // Code removed for clarity.
    BundleTable.EnableOptimizations = true;
}

Примечание

Если EnableOptimizations для атрибута не задано true значение или атрибут debug в элементе компиляции в файле falseWeb.config , файлы не будут объединяться или миниифицироваться. Кроме того, min-версия файлов не будет использоваться, будут выбраны полные отладочные версии. EnableOptimizations переопределяет атрибут debug в элементе compilation в файле Web.config .

Использование объединения и минификации с ASP.NET Web Forms и веб-страницами

Использование объединения и минификации с ASP.NET MVC

В этом разделе мы создадим проект ASP.NET MVC для изучения объединения и минификации. Сначала создайте новый интернет-проект ASP.NET MVC с именем MvcBM , не изменяя значения по умолчанию.

Откройте файл App\_Start\BundleConfig.cs и изучите RegisterBundles метод, используемый для создания, регистрации и настройки пакетов. В следующем коде показана часть RegisterBundles метода .

public static void RegisterBundles(BundleCollection bundles)
{
     bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
                 "~/Scripts/jquery-{version}.js"));
         // Code removed for clarity.
}

Приведенный выше код создает новый пакет JavaScript с именем ~/bundles/jquery, который включает все необходимые (отладочные или минифицированные, но не .файлы vsdoc) в папке Scripts, соответствующие строке wild карта "~/Scripts/jquery-{version}.js". Для ASP.NET MVC 4 это означает, что в конфигурации отладки файлjquery-1.7.1.js будет добавлен в пакет. В конфигурации выпуска будут добавленыjquery-1.7.1.min.js . Структура объединения соответствует нескольким общим соглашениям, таким как:

  • Если FileX.min.js и FileX.js существуют, выберите файл ".min" для выпуска.
  • Выбор версии, отличной от .min, для отладки.
  • Игнорирование файлов "-vsdoc" (например ,jquery-1.7.1-vsdoc.js), которые используются только IntelliSense.

Сопоставление {version} диких карта, показанное выше, используется для автоматического создания пакета jQuery с соответствующей версией jQuery в папке Scripts. В этом примере использование дикого карта обеспечивает следующие преимущества:

  • Позволяет использовать NuGet для обновления до новой версии jQuery без изменения предыдущего кода объединения или ссылок jQuery на страницах представлений.
  • Автоматически выбирает полную версию для конфигураций отладки и версию .min для сборок выпуска.

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

Следующий код заменяет локальный пакет jQuery пакетом CDN jQuery.

public static void RegisterBundles(BundleCollection bundles)
{
    //bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
    //            "~/Scripts/jquery-{version}.js"));

    bundles.UseCdn = true;   //enable CDN support

    //add link to jquery on the CDN
    var jqueryCdnPath = "https://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.7.1.min.js";

    bundles.Add(new ScriptBundle("~/bundles/jquery",
                jqueryCdnPath).Include(
                "~/Scripts/jquery-{version}.js"));

    // Code removed for clarity.
}

В приведенном выше коде jQuery будет запрашиваться из CDN в режиме выпуска, а отладочная версия jQuery будет получена локально в режиме отладки. При использовании CDN у вас должен быть резервный механизм на случай сбоя запроса CDN. В следующем фрагменте разметки в конце файла макета показан скрипт, добавленный для запроса jQuery в случае сбоя CDN.

</footer>

        @Scripts.Render("~/bundles/jquery")

        <script type="text/javascript">
            if (typeof jQuery == 'undefined') {
                var e = document.createElement('script');
                e.src = '@Url.Content("~/Scripts/jquery-1.7.1.js")';
                e.type = 'text/javascript';
                document.getElementsByTagName("head")[0].appendChild(e);

            }
        </script> 

        @RenderSection("scripts", required: false)
    </body>
</html>

Создание пакета

Метод класса IncludeBundle принимает массив строк, где каждая строка является виртуальным путем к ресурсу. Следующий код из RegisterBundles метода в файле App\_Start\BundleConfig.cs показывает, как несколько файлов добавляются в пакет:

bundles.Add(new StyleBundle("~/Content/themes/base/css").Include(
    "~/Content/themes/base/jquery.ui.core.css",
    "~/Content/themes/base/jquery.ui.resizable.css",
    "~/Content/themes/base/jquery.ui.selectable.css",
    "~/Content/themes/base/jquery.ui.accordion.css",
    "~/Content/themes/base/jquery.ui.autocomplete.css",
    "~/Content/themes/base/jquery.ui.button.css",
    "~/Content/themes/base/jquery.ui.dialog.css",
    "~/Content/themes/base/jquery.ui.slider.css",
    "~/Content/themes/base/jquery.ui.tabs.css",
    "~/Content/themes/base/jquery.ui.datepicker.css",
    "~/Content/themes/base/jquery.ui.progressbar.css",
    "~/Content/themes/base/jquery.ui.theme.css"));

Метод класса IncludeDirectoryBundle предоставляется для добавления всех файлов в каталог (и при необходимости всех подкаталогов), соответствующих шаблону поиска. API класса IncludeDirectoryBundle показан ниже:

public Bundle IncludeDirectory(
    string directoryVirtualPath,  // The Virtual Path for the directory.
    string searchPattern)         // The search pattern.

public Bundle IncludeDirectory(
    string directoryVirtualPath,  // The Virtual Path for the directory.
    string searchPattern,         // The search pattern.
    bool searchSubdirectories)    // true to search subdirectories.

Ссылки на пакеты в представлениях используют метод Render (Styles.Render для CSS и Scripts.Render JavaScript). В следующей разметке из файла Views\Shared\_Layout.cshtml показано, как представления веб-проектов по умолчанию ASP.NET ссылались на пакеты CSS и JavaScript.

<!DOCTYPE html>
<html lang="en">
<head>
    @* Markup removed for clarity.*@    
    @Styles.Render("~/Content/themes/base/css", "~/Content/css")
    @Scripts.Render("~/bundles/modernizr")
</head>
<body>
    @* Markup removed for clarity.*@
   
   @Scripts.Render("~/bundles/jquery")
   @RenderSection("scripts", required: false)
</body>
</html>

Обратите внимание, что методы Render принимают массив строк, поэтому вы можете добавить несколько пакетов в одну строку кода. Обычно требуется использовать методы Render, которые создают необходимый HTML-код для ссылки на ресурс. Метод можно использовать для Url создания URL-адреса ресурса без разметки, необходимой для ссылки на ресурс. Предположим, вы хотите использовать новый асинхронный атрибут HTML5. В следующем коде показано, как ссылаться на модернизатор с помощью Url метода .

<head>
    @*Markup removed for clarity*@
    <meta charset="utf-8" />
    <title>@ViewBag.Title - MVC 4 B/M</title>
    <link href="~/favicon.ico" rel="shortcut icon" type="image/x-icon" />
    <meta name="viewport" content="width=device-width" />
    @Styles.Render("~/Content/css")

   @* @Scripts.Render("~/bundles/modernizr")*@

    <script src='@Scripts.Url("~/bundles/modernizr")' async> </script>
</head>

Использование подстановочного знака "*" для выбора файлов

Виртуальный путь, указанный Include в методе, и шаблон поиска в IncludeDirectory методе могут принимать один подстановочный знак "*" в качестве префикса или суффикса в последнем сегменте пути. Строка поиска не учитывает регистр. Метод IncludeDirectory имеет возможность поиска подкаталогов.

Рассмотрим проект со следующими файлами JavaScript:

  • Scripts\Common\AddAltToImg.js
  • Scripts\Common\ToggleDiv.js
  • Scripts\Common\ToggleImg.js
  • Scripts\Common\Sub1\ToggleLinks.js

dir imag

В следующей таблице показаны файлы, добавленные в пакет с помощью подстановочного знака, как показано ниже.

Вызов Добавленные файлы или возникает исключение
Include("~/Scripts/Common/*.js") AddAltToImg.js, ToggleDiv.js, ToggleImg.js
Include("~/Scripts/Common/T*.js") Недопустимое исключение шаблона. Подстановочный знак допускается только в префиксе или суффиксе.
Include("~/Scripts/Common/*og.*") Недопустимое исключение шаблона. Допускается только один подстановочный знак.
Include("~/Scripts/Common/T*") ToggleDiv.js, ToggleImg.js
Include("~/Scripts/Common/*") Недопустимое исключение шаблона. Недопустимый сегмент с подстановочными знаками.
IncludeDirectory("~/Scripts/Common", "T*") ToggleDiv.js, ToggleImg.js
IncludeDirectory("~/Scripts/Common", "T*", true) ToggleDiv.js, ToggleImg.js, ToggleLinks.js

Явное добавление каждого файла в пакет обычно предпочтительнее загрузки файлов с подстановочными знаками по следующим причинам:

  • Добавление скриптов по подстановочным знакам по умолчанию позволяет загружать их в алфавитном порядке, что обычно не является нужным. Файлы CSS и JavaScript часто нужно добавлять в определенном (не алфавитном) порядке. Вы можете снизить этот риск, добавив пользовательскую реализацию IBundleOrderer , но явное добавление каждого файла менее подвержено ошибкам. Например, вы можете добавить новые ресурсы в папку в будущем, что может потребовать изменения реализации IBundleOrderer .

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

  • Css-файлы, которые импортируют другие файлы, приводят к тому, что импортированные файлы загружаются дважды. Например, следующий код создает пакет с большинством CSS-файлов темы jQuery UI, загруженных дважды.

    bundles.Add(new StyleBundle("~/jQueryUI/themes/baseAll")
        .IncludeDirectory("~/Content/themes/base", "*.css"));
    

    Дикий карта селектор "*.css" содержит каждый CSS-файл в папке, включая файл Content\themes\base\jquery.ui.all.css. Файл jquery.ui.all.css импортирует другие CSS-файлы.

Кэширование пакетов

Пакеты устанавливают заголовок HTTP Expires через год с момента создания пакета. При переходе на ранее просмотренную страницу Fiddler показывает, что IE не выполняет условный запрос для пакета, то есть отсутствуют HTTP-запросы GET от IE для пакетов и нет ответов HTTP 304 с сервера. Вы можете принудительно выполнить IE условный запрос для каждого пакета с помощью клавиши F5 (в результате для каждого пакета будет получен ответ HTTP 304). Вы можете принудительно выполнить полное обновление с помощью ^F5 (в результате для каждого пакета будет получен ответ HTTP 200).

На следующем рисунке показана вкладка Кэширование в области ответа Fiddler:

Изображение кэширования fiddler

Запрос
http://localhost/MvcBM_time/bundles/AllMyScripts?v=r0sLDicvP58AIXN_mc3QdyVvVj5euZNzdsa2N1PKvb81
для пакета AllMyScripts и содержит пару строк запроса v=r0sLDicvP58AIXN\_mc3QdyVvVj5euZNzdsa2N1PKvb81. Строка запроса v имеет маркер значения, который является уникальным идентификатором, используемым для кэширования. Пока пакет не изменится, приложение ASP.NET запросит пакет AllMyScripts с помощью этого маркера. Если какой-либо файл в пакете изменится, платформа оптимизации ASP.NET создаст новый токен, гарантируя, что запросы браузера для пакета получат последнюю версию пакета.

Если вы запускаете средства разработчика F12 IE9 и переходите на ранее загруженную страницу, IE неправильно отображает условные запросы GET, выполненные к каждому пакету и серверу, возвращающим HTTP 304. Вы можете узнать, почему в IE9 возникают проблемы с определением того, был ли выполнен условный запрос, в записи блога Использование CDN и Истекает для повышения производительности веб-сайта.

LESS, CoffeeScript, SCSS, Sass Bundling.

Платформа объединения и минификации предоставляет механизм для обработки промежуточных языков, таких как SCSS, Sass, LESS или Coffeescript, и применения преобразований, таких как минификация, к полученному пакету. Например, чтобы добавить less-файлы в проект MVC 4, выполните приведенные ниже действия.

  1. Создайте папку для содержимого LESS. В следующем примере используется папка Content\MyLess .

  2. Добавьте в проект пакет NuGet .less.
    Установка NuGet с точками

  3. Добавьте класс, реализующий интерфейс IBundleTransform . Для преобразования .less добавьте следующий код в проект.

    using System.Web.Optimization;
    
    public class LessTransform : IBundleTransform
    {
        public void Process(BundleContext context, BundleResponse response)
        {
            response.Content = dotless.Core.Less.Parse(response.Content);
            response.ContentType = "text/css";
        }
    }
    
  4. Создайте пакет файлов LESS с LessTransform помощью и преобразования CssMinify . Добавьте следующий код в RegisterBundles метод в файле App\_Start\BundleConfig.cs .

    var lessBundle = new Bundle("~/My/Less").IncludeDirectory("~/My", "*.less");
    lessBundle.Transforms.Add(new LessTransform());
    lessBundle.Transforms.Add(new CssMinify());
    bundles.Add(lessBundle);
    
  5. Добавьте следующий код в любые представления, которые ссылались на пакет LESS.

    @Styles.Render("~/My/Less");
    

Рекомендации по пакету

При создании пакетов рекомендуется включить слово "bundle" в качестве префикса в имя пакета. Это предотвратит возможный конфликт маршрутизации.

После обновления одного файла в пакете для параметра строки запроса пакета создается новый маркер, и при следующем запросе клиентом страницы, содержащей пакет, необходимо скачать полный пакет. В традиционной разметке, где каждый ресурс указан по отдельности, будет скачан только измененный файл. Ресурсы, которые часто изменяются, могут быть не очень хорошими кандидатами для объединения.

Объединение и минификация в первую очередь ускоряют загрузку первой страницы. После запроса веб-страницы браузер кэширует ресурсы (JavaScript, CSS и изображения), поэтому объединение и минификация не обеспечивают повышения производительности при запросе одной и той же страницы или страниц на том же сайте, запрашивающих одни и те же ресурсы. Если вы неправильно задали заголовок истекает для ресурсов и не используете объединение и минификации, эвристика актуальности браузеров помечает ресурсы устаревшими через несколько дней, а браузер потребует запроса на проверку для каждого ресурса. В этом случае объединение и минификация обеспечивают повышение производительности после первого запроса страницы. Дополнительные сведения см. в блоге Использование CDN и истекает для повышения производительности веб-сайта.

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

Пакеты должны быть секционированы по страницам, которым они нужны. Например, по умолчанию ASP.NET шаблон MVC для интернет-приложения создает пакет проверки jQuery отдельно от jQuery. Так как созданные по умолчанию представления не имеют входных данных и не публикуют значения, они не включают проверочный пакет.

Пространство System.Web.Optimization имен реализуется в System.Web.Optimization.dll. Она использует библиотеку WebGrease (WebGrease.dll) для минификации, которая, в свою очередь, использует Antlr3.Runtime.dll.

Я использую Twitter, чтобы сделать быстрые записи и поделиться ссылками. Мой дескриптор Twitter: @RickAndMSFT

Дополнительные ресурсы

Соавторы