В начале 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>
)
Но также получили широкое распространение и опасные техники, которые могут приводить к неправильному отображению документа и нарушению его семантики:
- Удаление декларации
- Замена длинных тегов на более короткие (например, теги
<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, а дистрибутивы можно загрузить через 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 на более короткую . |
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
) в элементе : webMarkupMin 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 на CodePlex
- Сайт WebMarkupMin Online
- Статья Артемия Лебедева «Паранойя оптимизатора»
- Руководство «Google HTML/CSS Style Guide» (перевод)
- Статья Юрия Зайцева «Optimizing HTML»
Автор: Taritsyn