Поделиться через


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

Рик Андерсон

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

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

B/M

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

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

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

Объединение

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

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

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

Минификация

Minification выполняет различные оптимизации кода для скриптов или 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/, "")) }

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

Исходное значение Переименован
imageTagAndImageID n
imageContext t
imageElement i

Влияние бундлинга и минификации

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

Использование 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 Меню правил выберите "Производительность", а затем "Имитация скоростей модема".)

Отладка пакета и minified JavaScript

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

  1. Перейдите на вкладку "Скрипт" и нажмите кнопку "Начать отладку ".
  2. Выберите пакет, содержащий функцию JavaScript, которую вы хотите выполнить отладку с помощью кнопки ресурсов.
    Снимок экрана: вкладка
  3. Отформатируйте мини-код JavaScript, нажав кнопку Изображение с значком кнопки "Конфигурация", а затем выберите формат JavaScript.
  4. В поле ввода скрипта поиска выберите имя функции, которую требуется выполнить отладку. На следующем рисунке в поле ввода скрипта поиска ввели AddAltToImg.
    Снимок экрана: вкладка

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

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

Объединение и минификации включено или отключено, задав значение атрибута отладки в элементе компиляции в файле 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 атрибут отладки в элементе компиляции в файле конфигурации Web.config не заданfalse, файлы не будут упаковываться или минироваться. Кроме того, не будет использоваться минимальная версия файлов, будут выбраны полные отладочные версии. EnableOptimizationsпереопределяет атрибут отладки в элементе компиляции в файле web.config

Использование Bundling и Minification с ASP.NET веб-формы и веб-страницами

Использование Bundling и Minification с 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/jquery-{version}.js". Для ASP.NET MVC 4 это означает, что конфигурация отладки будет добавлена в пакет jquery-1.7.1.js файла. В конфигурации выпуска jquery-1.7.1.min.js будет добавлен. Платформа объединение следует нескольким общим соглашениям, таким как:

  • Если FileX.min.js и FileX.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>

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

Метод класса Include Bundle принимает массив строк, где каждая строка — это виртуальный путь к ресурсу. Следующий код из 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"));

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

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 принимают массив строк, поэтому можно добавить несколько пакетов в одну строку кода. Как правило, вы хотите использовать методы отрисовки, которые создают необходимый 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:

  • Скрипты\Common\AddAltToImg.js
  • Скрипты\Common\ToggleDiv.js
  • Скрипты\Common\ToggleImg.js
  • Скрипты\Common\Sub1\ToggleLinks.js

Образ dir

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

Call Добавленные или исключения файлов
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 .

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

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

    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 С истекает годом после создания пакета. Если вы перейдете на ранее просматриваемую страницу, 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 создаст новый маркер, гарантируя, что запросы браузера для пакета получат последний пакет.

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

LESS, CoffeeScript, SCSS, Sass Bundling.

Платформа bundling и minification предоставляет механизм обработки промежуточных языков, таких как SCSS, Sass, LESS или Coffeescript, и применение преобразований, таких как минификации к результирующему пакету. Например, чтобы добавить В проект MVC 4 файлы меньшего размера, выполните приведенные ниже действия.

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

  2. Добавьте в проект пакет NuGet с меньшей точкой.
    Установка 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");
    

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

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

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

Объединение и минификация в первую очередь ускоряют загрузку первой страницы. После запроса веб-страницы браузер кэширует ресурсы (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

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

Соавторы