WebMarkupMin HTML Minifier – современный HTML-минимизатор для платформы .NET

в 10:48, , рубрики: .net, ASP.NET, asp.net mvc, ASP.NET Web Pages, html, xhtml, XML, Клиентская оптимизация, минимизация, метки: , , , , , , , ,

Логотип WebMarkupMin

В начале 2012 года я работал над серией статей о клиентской оптимизации в ASP.NET MVC для журнала MSDeveloper.RU. Всего было опубликовано 2 статьи: «Сжатие JS- и CSS-файлов» и «Менеджеры ресурсов», но в моих планах было написать еще 2 статьи: одну про оптимизацию графики, а вторую про минимизацию HTML-разметки и GZIP/Deflate-сжатие (далее просто HTTP-сжатие). К сожалению, эти планы не удалось воплотить в жизнь из-за нехватки свободного времени (в тот момент, я запускал проект Bundle Transformer) и последовавшего закрытия журнала.

Но недавно я решил вернуться к теме оптимизации HTML-разметки. После небольшого исследования я понял, что под .NET практически не существует полноценных HTML-минимизаторов. Все существующие .NET-решения производят лишь 2 операции: удаление ненужных пробельных символов и удаление HTML-комментариев, из-за чего они очень сильно проигрывают решениям с других платформ. Поэтому я решил написать собственный HTML-минимизатор для .NET, о котором и пойдет речь в данной статье.

Эволюция HTML-минимизаторов

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

Вопреки расхожему мнению, техники минимизации HTML-кода появились намного раньше, чем аналогичные техники для JavaScript. Уже в конце 1998 года Артемий Лебедев в 17-м параграфе ководства «Паранойя оптимизатора» описывал некоторые техники минимизации HTML-кода.

К началу 2000-х уже были известны многие техники минимизации HTML, которые актуальны и по сей день:

  1. Удаление ненужных пробельных символов (пробелы, табуляция и переводы строки)
  2. Удаление HTML-комментариев
  3. Удаление ненужных кавычек из атрибутов
  4. Удаление необязательных конечных тегов (например, </p> и </li>)

Но также получили широкое распространение и опасные техники, которые могут приводить к неправильному отображению документа и нарушению его семантики:

  1. Удаление декларации <!DOCTYPE …>
  2. Замена длинных тегов на более короткие (например, теги <strong> заменялись на <b>, а <em> на <i>)

В это же время появились первые HTML-минимизаторы. Только под ОС Windows существовало около десятка бесплатных и условно-бесплатных программ: HTML Shrinker, Absolute HTML Compressor, Оптимизатор HTML файлов, HTML Source Cleaner, Anetto HTML Optimize!, HTMLCompact, HTML Code Cleaner, HTMLOpt, Absolute HTML Optimizer и др.

Программа «Оптимизатор HTML файлов 1.1.0»

Рис. 1. Оптимизатор HTML файлов 1.1.0 – типичный HTML-минимизатор начала 2000-х

К середине 2000-х широкое распространение получил стандарт XHTML, который требовал написания HTML-кода в соответствии с синтаксическими правилами XML. Эти правила запрещали удаление ненужных кавычек из атрибутов и удаление необязательных конечных тегов. Таким образом, строгие правила XHTML и постоянно растущая пропускная способность привели к тому, что потребность в минимизации HTML-разметки постепенно отпала.

Но несколько лет назад из-за роста мобильного веба и появления стандарта HTML5 вновь возникла потребность в минимизации HTML-разметки. Стандарт HTML5 предоставляет гораздо больше возможностей для сокращения размера HTML-документа, чем HTML 4.01. Многие новые техники минимизации хорошо описаны в руководстве по оформлению HTML/CSS кода от Google и статье Юрия Зайцева «Optimizing HTML».

Все это привело к появлению новых мощных HTML-минимизаторов, ориентированных на HTML5. На данный момент наибольшую популярность получили 2 минимизатора: HtmlCompressor Сергея Ковальчука (написан на Java) и Experimental HTML Minifier Юрия Зайцева (написан на JavaScript).

Проект Web Markup Minifier

При создании Web Markup Minifier (сокращенно WebMarkupMin) я поставил себе задачу, создать современный HTML-минимизатор для платформы .NET и расширения для его интеграции с ASP.NET. WebMarkupMin — это Open Source-проект, исходный код которого опубликован на сайте CodePlex, а дистрибутивы можно загрузить через NuGet.

Помимо HTML-минимизатора в рамках проекта WebMarkupMin были также реализованы XHTML- и XML-минимизатор. Поскольку данная статья посвящена HTML-минимизации, то в ней будут приводиться только примеры работы с HTML-минимизатором.

Проект имеет следующую структуру:

  • Ядро — WebMarkupMin.Core
  • Внешние минимизаторы CSS- и JS-кода
    • WebMarkupMin.MsAjax
    • WebMarkupMin.Yui

  • Расширения для интеграции с ASP.NET
    • WebMarkupMin.Web
    • WebMarkupMin.Mvc
    • WebMarkupMin.WebForms

Ядро

Модуль WebMarkupMin.Core – это библиотека под .NET Framework 4.0, которая содержит инструменты для минимизации разметки. Данную библиотеку можно использовать в различных типах .NET-приложений: ASP.NET, Windows Forms, WPF и консольных приложениях. Поскольку все минимизаторы разметки поддерживают не только минимизацию документов, но и минимизацию отдельных фрагментов кода, то вы можете использовать WebMarkupMin для минимизации отдельных блоков контента (например, производить минимизацию текста статьи, при ее сохранении в административной части вашего сайта).

Минимизаторы разметки

Модуль WebMarkupMin.Core содержит 3 минимизатора разметки:

  1. HtmlMinifier. Производит минимизацию HTML- и XHTML-кода. В результате минимизации на выходе получается валидный HTML-код.
  2. XhtmlMinifier. Производит минимизацию HTML- и XHTML-кода. В результате минимизации на выходе получается код, соответствующий синтаксическим правилам XHTML.
  3. XmlMinifier. Производит минимизацию XML-кода.

Рассмотрим простейший пример использования класса HtmlMinifier:

namespace WebMarkupMin.Example.Console
{
   using System;
   using System.Collections.Generic;

   using WebMarkupMin.Core;
   using WebMarkupMin.Core.Minifiers;
   using WebMarkupMin.Core.Settings;

   class Program
   {
      static void Main(string[] args)
      {
         const string htmlInput = @"<!DOCTYPE html>
<html>
   <head>
      <meta charset=""utf-8"" />
      <title>Тестовый документ</title>
        <link href=""favicon.ico"" rel=""shortcut icon"" type=""image/x-icon"" />
        <meta name=""viewport"" content=""width=device-width"" />
      <link rel=""stylesheet"" type=""text/css"" href=""/Content/Site.css"" />
   </head>
   <body>
      <p>Какой-то текст…</p>

      <script src=""http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.9.1.min.js""></script>
      <script>(window.jquery) || document.write('<script src=""/Scripts/jquery-1.9.1.min.js""></script>');</script>
   </body>
</html>";

         var settings = new HtmlMinificationSettings
         {
            WhitespaceMinificationMode = WhitespaceMinificationMode.Aggressive,
            RemoveHttpProtocolFromAttributes = true,
            RemoveHttpsProtocolFromAttributes = true
         };

         var htmlMinifier = new HtmlMinifier(settings);

         MarkupMinificationResult result = htmlMinifier.Minify(htmlInput, 
            generateStatistics: true);
         if (result.Errors.Count == 0)
         {
            MinificationStatistics statistics = result.Statistics;
            if (statistics != null)
            {
               Console.WriteLine("Размер до минимизации: {0:N0} байт", 
                  statistics.OriginalSize);
               Console.WriteLine("Размер после минимизации: {0:N0} байт", 
                  statistics.MinifiedSize);
               Console.WriteLine("Экономия: {0:N2}%", 
                  statistics.SavedInPercent);
            }
            Console.WriteLine("Минимизированный код:{0}{0}{1}",
               Environment.NewLine, result.MinifiedContent);
         }
         else
         {
            IList<MinificationErrorInfo> errors = result.Errors;

            Console.WriteLine("Найдено {0:N0} ошибок:", errors.Count);
            Console.WriteLine();

            foreach (var error in errors)
            {
               Console.WriteLine("Строка {0}, Столбец {1}: {2}",
                  error.LineNumber, error.ColumnNumber, error.Message);
               Console.WriteLine();
            }
         }
      }
   }
}

Сначала мы создаем экземпляр класса HtmlMinificationSettings и переопределяем некоторые параметры HTML-минимизации. Затем передаем его в экземпляр класса HtmlMinifier через соответствующий параметр конструктора, после чего вызываем метод Minify со следующими параметрами: первый параметр содержит HTML-код, а второй – признак разрешающий генерацию статистической информации (значение по умолчанию – false, т.к. генерация статистики требует времени и дополнительных ресурсов сервера). Метод Minify возвращает объект типа MarkupMinificationResult, который имеет следующие свойства:

  • MinifiedContent – минимизированный HTML код;
  • Errors – список ошибок, которые возникли в процессе минимизации;
  • Warnings – список предупреждений о проблемах, которые были найдены во время минимизации;
  • Statistics – статистическая информация о минимизированном коде.

Если список ошибок пуст, то на консоль выводятся статистические данные и минимизированный код; в обратном случае выводится информация об ошибках.

А теперь подробно рассмотрим свойства класса HtmlMinificationSettings:

Табл. 1. Свойства класса HtmlMinificationSettings

Свойство Тип данных Значение по умолчанию Описание
WhitespaceMinificationMode Перечисление Medium Режим минимизации пробельных символов. Может принимать следующие значения:

  • None. Сохраняет пробельные символы в исходном виде.
  • Safe. Безопасная минимизация пробельных символов: удаляются пробелы из начала и конца документа; множественные пробельные символы заменяются одиночными пробелами; обрезаются оконечные пробелы у директивы DOCTYPE; обрезаются оконечные пробелы у внешнего и внутреннего содержимого невидимых тегов (теги html, head, body, meta, link, script и т.п.); обрезаются оконечные пробелы у внешнего содержимого несамостоятельных тегов (теги li, dt, dd, rt, rp, option, tr, td, th и т.п.).
  • Medium. Средний уровень минимизации пробельных символов: выполняются все операции безопасной минимизации + обрезаются оконечные пробелы у внешнего и внутреннего содержимого блочных (block) элементов.
  • Aggressive. Агрессивная минимизация пробельных символов: выполняются все операции среднего уровня минимизации + обрезаются оконечные пробелы у внутреннего содержимого строчных (inline) элементов и элементов inline-block.

RemoveHtmlComments Булевский true Флаг, отвечающий за удаление всех HTML-комментариев, за исключением условных комментариев Internet Explorer и noindex.
RemoveHtmlComments­FromScriptsAndStyles Булевский true Флаг, отвечающий за удаление HTML-комментариев из тегов script и style.
RemoveCdataSections­FromScriptsAndStyles Булевский true Флаг, отвечающий за удаление секций CDATA из тегов script и style.
UseShortDoctype Булевский true Флаг, отвечающий за замену существующей декларации doctype на более короткую <!DOCTYPE html>.
UseMetaCharsetTag Булевский true Флаг, отвечающий за замену тега <meta http-equiv="content-type" content="text/html; charset=…"> на тег <meta charset="…">.
EmptyTagRenderMode Перечисление NoSlash Режим рендеринга пустых тегов. Может принимать следующие значения:

  • NoSlash. Без слэша (например, <br>);
  • Slash. Со слэшем (например, <br/>);
  • SpaceAndSlash. Со слэшем и пробелом (например, <br />).

RemoveOptionalEndTags Булевский true Флаг, отвечающий за удаление необязательных конечных тегов (html, head, body, p, li, dt, dd, rt, rp, optgroup, option, colgroup, thead, tfoot, tbody, tr, th и td).
RemoveTagsWithoutContent Булевский false Флаг, отвечающий за удаление тегов, имеющий пустое содержимое, за исключением тегов textarea, tr, th, td, и тегов с атрибутами class, id, name, role, src и data-*.
CollapseBooleanAttributes Булевский true Флаг, отвечающий за «сворачивание» булевых атрибутов (например, checked="checked" сокращается до checked).
RemoveEmptyAttributes Булевский true Флаг, отвечающий за удаление пустых атрибутов (применяется только к следующим атрибутам: class, id, name, style, title, lang, dir, событийным атрибутам, атрибуту action тега form и атрибуту value тега input).
AttributeQuotesRemovalMode Перечисление Html5 Режим удаления кавычек в HTML-атрибутах. Может принимать следующие значения:

  • KeepQuotes. Сохраняет кавычки;
  • Html4. Производит удаление кавычек в соответствии со стандартом HTML 4.X;
  • Html5. Производит удаление кавычек в соответствии со стандартом HTML5.

RemoveRedundantAttributes Булевский true Флаг, отвечающий за удаление избыточных атрибутов:

  • <script <span style="background-color: yellow;">language="javascript"</span>>
  • <script src="…" <span style="background-color: yellow;">charset="…"</span>>
  • <link rel="stylesheet" <span style="background-color: yellow;">charset="…"</span>>
  • <form <span style="background-color: yellow;">method="get"</span>>
  • <input <span style="background-color: yellow;">type="text"</span>>
  • <a id="…" <span style="background-color: yellow;">name="…"</span>>
  • <area <span style="background-color: yellow;">shape="rect"</span>>

RemoveJsTypeAttributes Булевский true Флаг, отвечающий за удаление атрибутов type="text/javascript" из тегов script.
RemoveCssTypeAttributes Булевский true Флаг, отвечающий за удаление атрибутов type="text/css" из тегов style и link.
RemoveHttpProtocol­FromAttributes Булевский false Флаг, отвечающий за удаление префикса протокола HTTP (http:) из атрибутов, которые содержат URL (теги, помеченные атрибутом rel="external" игнорируются).
RemoveHttpsProtocol­FromAttributes Булевский false Флаг, отвечающий за удаление префикса протокола HTTPS (https:) из атрибутов, которые содержат URL (теги, помеченные атрибутом rel="external" игнорируются).
RemoveJsProtocol­FromAttributes Булевский true Флаг, отвечающий за удаление префикса псевдо-протокола javascript: из событийных атрибутов.
MinifyEmbeddedCssCode Булевский true Флаг, отвечающий за минимизацию CSS-кода в тегах style.
MinifyInlineCssCode Булевский true Флаг, отвечающий за минимизацию CSS-кода в атрибутах style.
MinifyEmbeddedJsCode Булевский true Флаг, отвечающий за минимизацию JS-кода в тегах script.
MinifyInlineJsCode Булевский true Флаг, отвечающий за минимизацию JS-кода в событийных атрибутах и гиперссылках с псевдо-протоколом javascript:.

Если в разных частях вашего приложения требуются одинаковые параметры HTML-минимизации, то вы можете указать их всего один раз в конфигурационном файле (App.config или Web.config) в элементе /configuration/webMarkupMin/core/html:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
   <configSections>
      <sectionGroup name="webMarkupMin">
         <section name="core"
            type="WebMarkupMin.Core.Configuration.CoreConfiguration, WebMarkupMin.Core" />
         …
      </sectionGroup>
      …
   </configSections>
   …
   <webMarkupMin xmlns="http://tempuri.org/WebMarkupMin.Configuration.xsd">
      <core>
         <html whitespaceMinificationMode="Medium" removeHtmlComments="true"
            removeHtmlCommentsFromScriptsAndStyles="true"
            removeCdataSectionsFromScriptsAndStyles="true"
            useShortDoctype="true" useMetaCharsetTag="true"
            emptyTagRenderMode="NoSlash" removeOptionalEndTags="true"
            removeTagsWithoutContent="false" collapseBooleanAttributes="true"
            removeEmptyAttributes="true"
            attributeQuotesRemovalMode="Html5" removeRedundantAttributes="true"
            removeJsTypeAttributes="true" removeCssTypeAttributes="true"
            removeHttpProtocolFromAttributes="false"
            removeHttpsProtocolFromAttributes="false"
            removeJsProtocolFromAttributes="true"
            minifyEmbeddedCssCode="true" minifyInlineCssCode="true"
            minifyEmbeddedJsCode="true" minifyInlineJsCode="true" />
         …
      </core>
      …
   </webMarkupMin>
   …
</configuration>

Чтобы получить экземпляр класса HtmlMinificationSettings со значениями из конфигурационного файла, используйте следующий код:

HtmlMinificationSettings settings = WebMarkupMinContext.Current.Markup.GetHtmlMinificationSettings();

Также вы можете создать экземпляр класса HtmlMinifier, который будет использовать настройки указанные в конфигурационном файле (параметры HTML-минимизации, а также зарегистрированные по умолчанию: CSS-минимизатор, JS-минимизатор и логгер):

HtmlMinifier htmlMinifier = WebMarkupMinContext.Current.Markup.CreateHtmlMinifierInstance();

Данный способ создания экземпляра HTML-минимизатора используется во всех модулях, отвечающих за интеграцию с ASP.NET.

Минимизаторы CSS- и JS-кода

HtmlMinifier и XhtmlMinifier помимо минимизации разметки поддерживают: минимизацию CSS-кода в тегах и атрибутах style, и минимизацию JavaScript-кода в тегах script, событийных атрибутах (например, onclick) и гиперссылках с псевдо-протоколом javascript:.

Минимизация CSS- и JS-кода производится классами, реализующими интерфейсы ICssMinifier и IJsMinifier из пространства имен WebMarkupMin.Core.Minifiers.

Ядро содержит два класса, которые реализуют интерфейс ICssMinifier:

  1. NullCssMinifier. Заглушка, которую следует использовать, когда в разметке отсутствуют встроенные стили.
  2. KristensenCssMinifier. Простейший минимизатор CSS-кода, созданный на базе Efficient stylesheet minifier Мэдса Кристенсена. Используется как миминизатор CSS-кода по умолчанию.

И два класса, которые реализуют интерфейс IJsMinifier:

  1. NullJsMinifier. Заглушка, которую следует использовать, когда в разметке отсутствуют встроенные скрипты.
  2. CrockfordJsMinifier. Простейший минимизатор JS-кода созданный на базе JSMin Дугласа Крокфорда. Используется как минимизатор JS-кода по умолчанию.

Экземпляры CSS- и JS-минимизаторов могут быть переданы в минимизатор разметки через его конструктор:

var kristensenCssMinifier = new KristensenCssMinifier();
var crockfordJsMinifier = new CrockfordJsMinifier();

var htmlMinifier = new HtmlMinifier(cssMinifier: kristensenCssMinifier, 
   jsMinifier: crockfordJsMinifier);

Если минимизатор разметки создается на основе параметров конфигурационного файла, то CSS- и JS-минимизаторы можно передать в минимизатор разметки путем регистрации их в конфигурационном файле качестве минимизаторов по умолчанию:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
   <configSections>
      <sectionGroup name="webMarkupMin">
         <section name="core"
            type="WebMarkupMin.Core.Configuration.CoreConfiguration, WebMarkupMin.Core" />
         …
      </sectionGroup>
      …
   </configSections>
   …  
   <webMarkupMin xmlns="http://tempuri.org/WebMarkupMin.Configuration.xsd">
      <core>
         …
         <css defaultMinifier="KristensenCssMinifier">
            <minifiers>
               <add name="NullCssMinifier" 
                  displayName="Null CSS Minifier"
                  type="WebMarkupMin.Core.Minifiers.NullCssMinifier, WebMarkupMin.Core" />
               <add name="KristensenCssMinifier" 
                  displayName="Mads Kristensen's CSS minifier"
                  type="WebMarkupMin.Core.Minifiers.KristensenCssMinifier, WebMarkupMin.Core" />
            </minifiers>
         </css>
         <js defaultMinifier="CrockfordJsMinifier">
            <minifiers>
               <add name="NullJsMinifier" 
                  displayName="Null JS Minifier"
                  type="WebMarkupMin.Core.Minifiers.NullJsMinifier, WebMarkupMin.Core" />
               <add name="CrockfordJsMinifier" 
                  displayName="Douglas Crockford's JS Minifier"
                  type="WebMarkupMin.Core.Minifiers.CrockfordJsMinifier, WebMarkupMin.Core" />
            </minifiers>
         </js>
         …
      </core>
      …
   </webMarkupMin>
   …
</configuration>

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

ICssMinifier cssMinifier = 
   WebMarkupMinContext.Current.Code.CreateCssMinifierInstance("KristensenCssMinifier");
IJsMinifier jsMinifier = 
   WebMarkupMinContext.Current.Code.CreateJsMinifierInstance("CrockfordJsMinifier");

Если вы просто хотите создать экземпляры CSS- и JS-минимизаторов, которые зарегистрированы как минимизаторы по умолчанию, то это можно сделать следующим образом:

ICssMinifier cssMinifier = 
   WebMarkupMinContext.Current.Code.CreateDefaultCssMinifierInstance();
IJsMinifier jsMinifier = 
   WebMarkupMinContext.Current.Code.CreateDefaultJsMinifierInstance();

Логгеры

Помимо ручной обработки ошибок и предупреждений в WebMarkupMin также предусмотрена возможность подключения логгеров, с помощью которых вы можете централизованно записывать ошибки и предупреждения в собственные журналы. Логгером может быть любой класс, реализующий интерфейс ILogger или наследующий базовый класс LoggerBase из пространства имен WebMarkupMin.Core.Loggers.

Ядро содержит два класса, которые реализуют интерфейс ILogger:

  1. NullLogger. Заглушка, которую следует использовать, когда вы самостоятельно обрабатываете значения свойств Errors и Warnings класса MarkupMinificationResult.
  2. ThrowExceptionLogger. Если во время минимизации возникает ошибка, то класс ThrowExceptionLogger генерирует исключение типа MarkupMinificationException.

Экземпляр логгера можно передать в минимизатор разметки через его конструктор:

var htmlMinifier = new HtmlMinifier(logger: new ThrowExceptionLogger());

Если минимизатор разметки создается на основе параметров конфигурационного файла, то логгер можно передать в него путем регистрации в конфигурационном файле в качестве логгера по умолчанию:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
   <configSections>
      <sectionGroup name="webMarkupMin">
         <section name="core"
            type="WebMarkupMin.Core.Configuration.CoreConfiguration, WebMarkupMin.Core" />
         …
      </sectionGroup>
      …
   </configSections>
   …
   <webMarkupMin xmlns="http://tempuri.org/WebMarkupMin.Configuration.xsd">
      <core>
         …
         <logging defaultLogger="ThrowExceptionLogger">
            <loggers>
               <add name="NullLogger"
                  displayName="Null Logger"
                  type="WebMarkupMin.Core.Loggers.NullLogger, WebMarkupMin.Core" />
               <add name="ThrowExceptionLogger" displayName="Throw exception logger"
                  type="WebMarkupMin.Core.Loggers.ThrowExceptionLogger, WebMarkupMin.Core" />
            </loggers>
         </logging>
      </core>
      …
   </webMarkupMin>
   …
</configuration>

Если логгер зарегистрирован в конфигурационном файле, то его экземпляр можно создать следующим образом:

ILogger logger = WebMarkupMinContext.Current.CreateLoggerInstance("ThrowExceptionLogger");

Соответственно для создания логгера, зарегистрированного как логгер по умолчанию, можно использовать следующий код:

ILogger logger = WebMarkupMinContext.Current.CreateDefaultLoggerInstance();

Также предусмотрена возможность использования единственного экземпляра логгера для всего приложения:

ILogger logger = WebMarkupMinContext.Current.GetLoggerInstance("ThrowExceptionLogger");

и

ILogger logger = WebMarkupMinContext.Current.GetDefaultLoggerInstance();

Внешние минимизаторы CSS- и JS-кода

Встроенные минимизаторы CSS- и JS-кода производят лишь простые оптимизации и не могут обеспечить высокую степень сжатия. Для решения данной проблемы были созданы дополнительные модули, содержащие адаптеры для популярных в .NET сообществе минимизаторов: Microsoft Ajax Minifier и YUI Compressor for .Net.

Модуль WebMarkupMin.MsAjax содержит два адаптера-минимизатора: MsAjaxCssMinifier и MsAjaxJsMinifier. Аналогичным образом организован и модуль WebMarkupMin.Yui: YuiCssMinifier и YuiJsMinifier.

Адаптеры-минимизаторы можно передать в минимизатор разметки, с помощью тех же самых механизмов, что и встроенные минимизаторы.

Кроме того, настройки вышеперечисленных внешних минимизаторов можно менять в секциях webMarkupMin/msAjax и webMarkupMin/yui конфигурационного файла (тем, кто пользуется Bundle Transformer, это покажется знакомым).

Расширения для интеграции с ASP.NET

WebMarkupMin: Web Extensions

Модуль WebMarkupMin.Web работает на уровне ядра ASP.NET, и поэтому может использоваться в любом из существующих ASP.NET-фреймворков: Web Forms, MVC и Web Pages.

WebMarkupMin.Web содержит классы HTTP-модулей, которые позволяют минимизировать и сжимать код, который генерируется средствами ASP.NET:

  1. HtmlMinificationModule. Производит минимизацию содержимого HTTP-ответов с типом содержимого text/html средствами HTML Minifier.
  2. XhtmlMinificationModule. Производит минимизацию содержимого HTTP-ответов с типом содержимого text/html или application/xhtml+xml средствами XHTML Minifier.
  3. XmlMinificationModule. Производит минимизацию содержимого HTTP-ответов с типом содержимого на базе XML (за исключением application/xhtml+xml) средствами XML Minifier.
  4. CompressionModule. Производит HTTP-сжатие содержимого HTTP-ответов с текстовым типом содержимого.

Перечисленные выше HTTP-модули могут обрабатывать только GET-запросы и ответы с кодом состояния равным 200. Следует также отметить, что HtmlMinificationModule и XhtmlMinificationModule не могут использоваться вместе.

Рассмотрим пример регистрации HTTP-модулей в файле Web.config:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
   …
   <system.webServer>
      <modules>
         <add name="HtmlMinificationModule"
            type="WebMarkupMin.Web.HttpModules.HtmlMinificationModule, WebMarkupMin.Web" />
         <add name="CompressionModule"
            type="WebMarkupMin.Web.HttpModules.CompressionModule, WebMarkupMin.Web" />
         …
      </modules>
      …
   </system.webServer>
   …
</configuration>

Кроме того, поведением этих HTTP-модулей можно управлять с помощью конфигурационной секции webMarkupMin/webExtensions:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
   <configSections>
      <sectionGroup name="webMarkupMin">
         <section name="core" 
            type="WebMarkupMin.Core.Configuration.CoreConfiguration, WebMarkupMin.Core" />
         <section name="webExtensions"
            type="WebMarkupMin.Web.Configuration.WebExtensionsConfiguration, WebMarkupMin.Web" />
         …
      </sectionGroup>
      …
   </configSections>
   …
   <webMarkupMin xmlns="http://tempuri.org/WebMarkupMin.Configuration.xsd">
      …
      <webExtensions enableMinification="true"
         disableMinificationInDebugMode="true"
         enableCompression="true" disableCompressionInDebugMode="true"
         maxResponseSize="100000" />
      …
   </webMarkupMin>
   …
</configuration>

Подробно рассмотрим все свойства конфигурационной секции webExtensions:

Табл. 2 Свойства конфигурационной секции webExtensions

Свойство Тип данных Значение по умолчанию Описание
enableMinification Булевский true Включает минимизацию разметки.
disableMinificationInDebugMode Булевский true Отключаем минимизацию разметки в режиме отладки.
enableCompression Булевский true Включает HTTP-сжатие текстового содержимого.
disableCompressionInDebugMode Булевский true Отключает HTTP-сжатие текстового содержимого в режиме отладки.
maxResponseSize Целое число 100 000 Максимальный размер HTTP-ответа (в байтах), при превышении которого отключается минимизация разметки.

Данные настройки, также используются другими модулями WebMarkupMin: WebMarkupMin.Mvc и WebMarkupMin.WebForms.

Несмотря на то, что описанные выше HTTP-модули можно использовать совместно с любым ASP.NET-фреймворком, для MVC и Web Forms все же рекомендуется использовать более специализированные решения, потому что HTTP-модули не поддерживают кэширование. Использование HTTP-модулей подойдет для небольших сайтов, написанных на ASP.NET Web Pages.

WebMarkupMin: ASP.NET MVC Extensions

Модуль WebMarkupMin.Mvc ориентирован на использование в веб-приложениях, написанных на ASP.NET MVC версий 3 и 4.

WebMarkupMin.Mvc содержит 4 фильтра:

  1. MinifyHtmlAttribute. Производит минимизацию результата действия средствами HTML Minifier. Если результат действия имеет тип содержимого отличный от text/html, то генерируется исключение типа InvalidContentTypeException.
  2. MinifyXhtmlAttribute. Производит минимизацию результата действия средствами XHTML Minifier. Если результат действия имеет тип содержимого отличный от text/html или application/xhtml+xml, то генерируется исключение типа InvalidContentTypeException.
  3. MinifyXmlAttribute. Производит минимизацию результата действия средствами XML Minifier. Если результат действия имеет тип содержимого основанный не на XML, то генерируется исключение типа InvalidContentTypeException.
  4. CompressContentAttribute. Производит HTTP-сжатие результата действия.

Приведу простой пример использования фильтров:

namespace WebMarkupMin.Example.Mvc.Controllers
{
   using System.Web.Mvc;

   using Infrastructure.ActionResults;
   using WebMarkupMin.Mvc.ActionFilters;

   public class HomeController : Controller
   {
      [CompressContent]
      [MinifyHtml]
      [OutputCache(CacheProfile = "CacheCompressedContent5Minutes")]
      public ActionResult Index()
      {
         …
      }
      …      
   }
}

Поскольку минимизация разметки и HTTP-сжатие требуют некоторого времени и ресурсов сервера, то мы кэшируем результат их работы с помощью фильтра OutputCacheAttribute.

WebMarkupMin: ASP.NET Web Forms Extensions

Модуль WebMarkupMin.WebForms ориентирован на использование в веб-приложениях, написанных на ASP.NET Web Forms версий 4.0 и 4.5.

WebMarkupMin.WebForms содержит 3 класса страниц Web Forms:

  1. CompressedPage. Поддерживает только HTTP-сжатие.
  2. MinifiedAndCompressedHtmlPage. Поддерживает HTML-минимизацию и HTTP-сжатие.
  3. MinifiedAndCompressedXhtmlPage. Поддерживает XHTML-минимизацию и HTTP-сжатие.

Для того чтобы добавить поддержку HTML-минимизации и HTTP-сжатия в страницу Web Forms, нужно просто ее наследовать от класса MinifiedAndCompressedHtmlPage:

namespace WebMarkupMin.Example.WebForms
{
   using System;

   using WebMarkupMin.WebForms.Pages;

   public partial class Contact : MinifiedAndCompressedHtmlPage
   {
      …
   }
}

Если нужно отключить минимизацию разметки и HTTP-сжатие во время возврата формы, то можно воспользоваться свойствами страницы EnableMinification и EnableCompression:

private void Page_PreLoad(object sender, EventArgs e)
{
   if (IsPostBack)
   {
      EnableMinification = false;
      EnableCompression = false;
   }
}

Результат минимизации разметки и HTTP-сжатия рекомендуется кэшировать с помощью директивы OutputCache:

<%@ Page Title="Contact" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="Contact.aspx.cs" Inherits="WebMarkupMin.Example.WebForms.Contact" %>
<%@ OutputCache CacheProfile="CacheCompressedContent5Minutes" VaryByParam="*" %>
…

Также в модуле WebMarkupMin.WebForms есть аналогичные классы для мастер-страниц: CompressedMasterPage, MinifiedAndCompressedHtmlMasterPage и MinifiedAndCompressedXhtmlMasterPage.

Для добавления поддержки HTML-минимизации и HTTP-сжатия в мастер-страницу, ее нужно наследовать от класса MinifiedAndCompressedHtmlMasterPage:

namespace WebMarkupMin.Example.WebForms
{
   using System;
   using System.Web.UI;

   using WebMarkupMin.WebForms.MasterPages;

   public partial class Site : MinifiedAndCompressedHtmlMasterPage
   {
      …
   }
}

Следует также отметить, что классы страниц и мастер-страниц не могут использоваться совместно.

Эффективность минимизации

При минимизации разметки средствами HTML-минимизатора из WebMarkupMin можно сократить размер кода на 25-40%. Некоторые специалисты по клиентской оптимизации могут сказать, что при использовании HTTP-сжатия можно сэкономить в несколько раз больше. В чем-то они окажутся правы, но ничто не мешает использовать HTML-минимизацию и HTTP-сжатие совместно. Следует также помнить, что у минимизации кода по сравнению со сжатием, есть одно существенное преимущество: минимизированный код сразу обрабатывается браузером (при использовании HTTP-сжатия требуется этап распаковки).

Чтобы продемонстрировать возможности HTML-минимизатора возьмем в качестве примера главные страницы четырех популярных в Рунете сайтов, написанных на ASP.NET, и минимизируем их. Мы будем использовать HTML-минимизатор с параметрами минимизации по умолчанию, в качестве CSS-минимизатора установим YuiCssMinifier, а в качестве JS-минимизатора — MsAjaxJsMinifier.

Табл. 3. Результаты HTML-минимизации без HTTP-сжатия

Название сайта Адрес Размер до минимизации* Размер после минимизации* Экономия
Афиша www.afisha.ru 162,28 КБ 110,64 КБ 31,82%
МТС www.mts.ru 80,56 КБ 48,50 КБ 39,80%
OZON www.ozon.ru 107,32 КБ 62,21 КБ 42,03%
Workle www.workle.ru 115,26 КБ 72,94 КБ 36,72%

Табл. 4. Результаты HTML-минимизации с включенным HTTP-сжатием (в данном примере использовалось GZIP-сжатие)

Название сайта Адрес Размер до минимизации* Размер после минимизации* Экономия
Афиша www.afisha.ru 30,01 КБ 25,47 КБ 15,14%
МТС www.mts.ru 19,08 КБ 14,15 КБ 25,86%
OZON www.ozon.ru 16,58 КБ 14,23 КБ 14,22%
Workle www.workle.ru 19,06 КБ 17,31 КБ 9,15%

* — при расчетах предполагалось, что 1 Кбайт = 1 024 байт

Из табл. 3 видно, что при использовании минимизации без HTTP-сжатия можно сократить размер HTML-документа в среднем на 37,59%, а это очень хороший показатель.

При использовании минимизации вместе с HTTP-сжатием (табл. 4) вклад минимизации в снижение размера страницы уже не такой существенный. Но в тоже время для Афиши и МТС мы получили очень неплохой результат в абсолютном выражении: выигрыш от минимизации 4,54 и 4,93 Кбайт соответственно. Если учитывать, что эти 2 сайта имеют очень высокую посещаемость, то экономия исходящего трафика может оказаться существенной.

Вы можете самостоятельно провести аналогичные измерения для своего сайта, используя онлайн-версию HTML-минимизатора на сайте WebMarkupMin Online.

Онлайн-версия HTML-минимизатора на сайте WebMarkupMin Online

Рис. 2. Онлайн-версия HTML-минимизатора на сайте WebMarkupMin Online

Ссылки

  1. Страница проекта WebMarkupMin на CodePlex
  2. Сайт WebMarkupMin Online
  3. Статья Артемия Лебедева «Паранойя оптимизатора»
  4. Руководство «Google HTML/CSS Style Guide» (перевод)
  5. Статья Юрия Зайцева «Optimizing HTML»

Автор: Taritsyn

Источник

Поделиться

* - обязательные к заполнению поля