
В начале 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, которые актуальны и по сей день:
- Удаление ненужных пробельных символов (пробелы, табуляция и переводы строки)
- Удаление HTML-комментариев
- Удаление ненужных кавычек из атрибутов
- Удаление необязательных конечных тегов (например,
</p>и</li>)
Но также получили широкое распространение и опасные техники, которые могут приводить к неправильному отображению документа и нарушению его семантики:
- Удаление декларации
<!DOCTYPE …> - Замена длинных тегов на более короткие (например, теги
<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 и др.

Рис. 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 (в 2016 году проект полностью переехал на GitHub), а дистрибутивы можно загрузить через 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 минимизатора разметки:
- HtmlMinifier. Производит минимизацию HTML- и XHTML-кода. В результате минимизации на выходе получается валидный HTML-код.
- XhtmlMinifier. Производит минимизацию HTML- и XHTML-кода. В результате минимизации на выходе получается код, соответствующий синтаксическим правилам XHTML.
- 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 |
Режим минимизации пробельных символов. Может принимать следующие значения:
|
RemoveHtmlComments |
Булевский | true |
Флаг, отвечающий за удаление всех HTML-комментариев, за исключением условных комментариев Internet Explorer и noindex. |
RemoveHtmlCommentsFromScriptsAndStyles |
Булевский | true |
Флаг, отвечающий за удаление HTML-комментариев из тегов script и style. |
RemoveCdataSectionsFromScriptsAndStyles |
Булевский | true |
Флаг, отвечающий за удаление секций CDATA из тегов script и style. |
UseShortDoctype |
Булевский | true |
Флаг, отвечающий за замену существующей декларации doctype на более короткую — <!DOCTYPE html>. |
UseMetaCharsetTag |
Булевский | true |
Флаг, отвечающий за замену тега <meta http-equiv="content-type" content="text/html; charset=…"> на тег <meta charset="…">. |
EmptyTagRenderMode |
Перечисление | NoSlash |
Режим рендеринга пустых тегов. Может принимать следующие значения:
|
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-атрибутах. Может принимать следующие значения:
|
RemoveRedundantAttributes |
Булевский | true |
Флаг, отвечающий за удаление избыточных атрибутов:
|
RemoveJsTypeAttributes |
Булевский | true |
Флаг, отвечающий за удаление атрибутов type="text/javascript" из тегов script. |
RemoveCssTypeAttributes |
Булевский | true |
Флаг, отвечающий за удаление атрибутов type="text/css" из тегов style и link. |
RemoveHttpProtocolFromAttributes |
Булевский | false |
Флаг, отвечающий за удаление префикса протокола HTTP (http:) из атрибутов, которые содержат URL (теги, помеченные атрибутом rel="external" игнорируются). |
RemoveHttpsProtocolFromAttributes |
Булевский | false |
Флаг, отвечающий за удаление префикса протокола HTTPS (https:) из атрибутов, которые содержат URL (теги, помеченные атрибутом rel="external" игнорируются). |
RemoveJsProtocolFromAttributes |
Булевский | 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:
- NullCssMinifier. Заглушка, которую следует использовать, когда в разметке отсутствуют встроенные стили.
- KristensenCssMinifier. Простейший минимизатор CSS-кода, созданный на базе Efficient stylesheet minifier Мэдса Кристенсена. Используется как миминизатор CSS-кода по умолчанию.
И два класса, которые реализуют интерфейс IJsMinifier:
- NullJsMinifier. Заглушка, которую следует использовать, когда в разметке отсутствуют встроенные скрипты.
- 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:
- NullLogger. Заглушка, которую следует использовать, когда вы самостоятельно обрабатываете значения свойств
ErrorsиWarningsклассаMarkupMinificationResult. - 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:
- HtmlMinificationModule. Производит минимизацию содержимого HTTP-ответов с типом содержимого
text/htmlсредствами HTML Minifier. - XhtmlMinificationModule. Производит минимизацию содержимого HTTP-ответов с типом содержимого
text/htmlилиapplication/xhtml+xmlсредствами XHTML Minifier. - XmlMinificationModule. Производит минимизацию содержимого HTTP-ответов с типом содержимого на базе XML (за исключением
application/xhtml+xml) средствами XML Minifier. - 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 фильтра:
- MinifyHtmlAttribute. Производит минимизацию результата действия средствами HTML Minifier. Если результат действия имеет тип содержимого отличный от
text/html, то генерируется исключение типаInvalidContentTypeException. - MinifyXhtmlAttribute. Производит минимизацию результата действия средствами XHTML Minifier. Если результат действия имеет тип содержимого отличный от
text/htmlилиapplication/xhtml+xml, то генерируется исключение типаInvalidContentTypeException. - MinifyXmlAttribute. Производит минимизацию результата действия средствами XML Minifier. Если результат действия имеет тип содержимого основанный не на XML, то генерируется исключение типа
InvalidContentTypeException. - 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:
- CompressedPage. Поддерживает только HTTP-сжатие.
- MinifiedAndCompressedHtmlPage. Поддерживает HTML-минимизацию и HTTP-сжатие.
- 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.

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