Главная > .NET, JavaScript > Клиентская оптимизация в ASP.NET MVC 3. Сжатие JS- и CSS-файлов

Клиентская оптимизация в ASP.NET MVC 3. Сжатие JS- и CSS-файлов

Первоначально данная статья была опубликована в январском номере журнала MSDeveloper.RU.

Содержание

  1. Сжатие JS- и CSS-файлов
  2. Менеджеры ресурсов

В своей предыдущей статье «Облегчаем работу с JS- и CSS-кодом в Visual Studio» я уже говорил о том, что практически в любом современном веб-приложении среднего размера содержатся десятки JavaScript- и CSS-файлов. В прошлый раз я рассказал, как облегчить работу с кодом, который расположен в этих файлах. Рассмотрим эту проблему под другим углом: большой объем кода внутри этих файлов, к которому мы еще добавили регионы, теги reference и другие документирующие комментарии, приводит к увеличению времени загрузки веб-страниц и увеличивает объем трафика, который проходит от сервера к браузеру пользователя. Если время ожидания загрузки веб-страницы будет слишком долгим, то пользователь сайта начнет себя чувствовать некомфортно и может покинуть сайт. Кроме того, проблемы могут возникнуть и у владельца сайта, если исходящий с сайта трафик превысит заранее оговоренный с хостинговой компанией объем.

В данной серии статей я хочу рассказать о клиентской оптимизации веб-приложений, написанных на ASP.NET MVC 3. Клиентская оптимизация – это комплекс мер по увеличению скорости загрузки клиентской части веб-страницы (HTML, CSS, JavaScript и графика). Понятие клиентская оптимизация известно давно, но впервые о ней серьезно заговорили в конце 2007 года после публикации статьи «13 простых правил для ускорения вашего веб-сайта» («Thirteen Simple Rules for Speeding Up Your Web Site»), написанной командой Exceptional Performance (Исключительная производительность) компании Yahoo!. За прошедшие четыре года 13 простых правил превратились в 35 практических рекомендаций, с которыми можно ознакомиться в статье «Best Practices for Speeding Up Your Web Site».

В данной статье мы рассмотрим и применим на практике только одну рекомендацию команды Exceptional Performance – минимизацию JS- и CSS-кода.

Минимизация (сжатие) JS- и CSS-файлов

С помощью минимизации (также используется синоним — минификация) можно существенно уменьшить размеры JS- и CSS-файлов и тем самым сократить время загрузки веб-страницы.

В процессе минимизации производятся следующие действия:

  1. Из кода удаляются все комментарии (за исключением CSS-хаков)
  2. Из кода удаляются лишние пробелы, символы табуляции и переноса строки
  3. Длинные программные инструкции (или стилевые правила) заменяются на более короткие эквиваленты

При минимизации JS-кода также заменяются имена параметров пользовательских методов, локальных переменных и функций на более короткие (как правило, одно-символьные).

При минимизации JS-файлов можно добиться уменьшения размера до 30-50%. CSS-файлы сжимаются хуже – 40-75%. Во многом данный показатель зависит от количества комментариев к коде.

Утилиты, осуществляющие минимизацию

Существует несколько широко известных утилит для минимизации JS-кода: JSMin, Google Closure Compiler, Packer и UglifyJS. Из утилит для минимизации CSS-кода вспоминается только CSSTidy. Также существуют универсальные утилиты, которые сжимают и JS-код, и CSS-код: YUI Compressor и Microsoft Ajax Minifier.

Некоторые из перечисленных утилит используются в популярных менеджерах ресурсов и запускают процедуру минимизации при первом обращении к ресурсу (JS- или CSS-файлу). Но такой подход не безопасен, т.к. в процессе минимизации могут происходить ошибки, которые могут быть обнаружены только при развертывании веб-приложения на сервере. К тому же, многие JS-библиотеки поставляются вместе с минимизированными версиями, работоспособность которых протестирована самими разработчиками. Производить минимизацию таких библиотек с помощью менеджера ресурсов просто не имеет смысла.

Лучше производить минимизацию ресурсов в IDE, в данном случае в Visual Studio. Минимизация производится в процессе сборки (компиляции) проекта или при сохранении измененного файла. Такой подход позволяет протестировать минимизированный код до публикации на производственном сервере (например, работоспособность минимизированного JS-кода можно проверить с помощью юнит-тестов).

Microsoft Ajax Minifier

Страница минимизатора Microsoft Ajax Minifier на сайте CodePlex

В своих проектах я обычно использую Microsoft Ajax Minifier (далее Ajax Minifier). Изначально данный продукт разрабатывался для минимизации JS-кода библиотеки Microsoft ASP.NET AJAX и поэтому не получил такого широкого распространения как YUI Compressor и Google Closure Compiler. Но, на мой взгляд, Ajax Minifier – это очень мощное средство минимизации, которое ни в чем не уступает своим конкурентам.

На нескольких официальных сайтах компании Microsoft в разделах, посвященных Ajax Minifier, до сих пор стоят ссылки на старую версию продукта (версию 4.0). При использовании данной версии для минимизации CSS-кода иногда возникают ошибки из-за того, что Ajax Minifier, не знает как обрабатывать некоторые новые или специфичные для конкретных браузеров синтаксические конструкции (например, псевдо-класс ::-moz-focus-inner). В текущей версии (4.40) подобные ошибки уже не возникают. Самую последнюю версию Ajax Minifier можно скачать по адресу — http://ajaxmin.codeplex.com.

Его можно использовать тремя разными способами: как утилиту командной строки (AjaxMin.exe), .NET-библиотеку (AjaxMin.dll) или задачу MSBuild (обеспечивает интеграцию с Visual Studio). Соответственно мы будем использовать третий способ – задачу MSBuild. При использовании задачи MSBuild минимизация файлов производится во время сборки (компиляции) проекта.

Чтобы добавить задачу MSBuild в проект ASP.NET-приложения нужно выполнить следующие действия:

  1. В Solution Explorer щелкните правой кнопкой мыши по проекту и в контекстном меню выберите пункт Unload Project. После чего пиктограмма проекта станет серой.
  2. Снова щелкните по проекту правой кнопкой мыши и выберите в контекстном меню пункт Edit <ИмяПроекта>.csproj. В окне XML-редактора откроется код проекта.

    Контекстное меню выгруженного проекта
    Рис. 1. Контекстное меню выгруженного проекта

  3. Найдите закрывающийся тег элемента ProjectExtensions и добавьте после него следующий код (в качестве примера привожу код из своего проекта):
    <Import Project="$(MSBuildExtensionsPath)\Microsoft\
    MicrosoftAjax\ajaxmin.tasks" />
    <Target Name="AfterBuild">
        <ItemGroup>
            <JS Include="**\*.js" Exclude="**\*.min.js;
    **\ckeditor\*.js;**\ckeditor\**\*.js;**\jquery-1.7.1.js;
    **\jquery.validate.js;**\jquery.unobtrusive-ajax.js;
    **\jquery.validate.unobtrusive.js;**\jquery.ba-bbq.js;
    **\MicrosoftAjax.debug.js;**\MicrosoftAjax.js;**\html5.js;" />
        </ItemGroup>
        <ItemGroup>
            <CSS Include="**\*.css" Exclude="**\*.min.css;
    **\ckeditor\*.css;**\ckeditor\**\*.css;" />
        </ItemGroup>
        <AjaxMin
            JsSourceFiles="@(JS)"
            JsSourceExtensionPattern="\.js$" 
            JsTargetExtension=".min.js" 
            JsCollapseToLiteral="true" 
            JsCombineDuplicateLiterals="true" 
            JsEvalTreatment="Ignore" 
            JsLocalRenaming="KeepLocalizationVars" 
            JsMacSafariQuirks="true" 
            JsRemoveUnneededCode="true" 
            JsStripDebugStatements="true" 
            JsOutputMode="SingleLine" 
            CssSourceFiles="@(CSS)" 
            CssSourceExtensionPattern="\.css$" 
            CssTargetExtension=".min.css" 
            CssColorNames="Hex" 
            CssCommentMode="Hacks" 
            CssTermSemicolons="true"
            CssExpandOutput="false" />
     </Target>
    

    Немного поясню, что делает данный код. Элемент Import подключает задачи, которые были установлены вместе с Ajax Minifier. В элементе Target указано, что данная задача будет запущена после успешной компиляции проекта.
    Внутри элементов ItemGroup задаются группы файлов, которые мы хотим минимизировать. Элементы JS и CSS – это произвольные названия групп файлов, которые можно переименовать на свое усмотрение (например, Scripts и StyleSheets). В атрибутах Include указан шаблон пути к обрабатываемым файлам: **\*.js и **\*.css. В атрибутах Exclude заданы шаблоны путей, которые нужно исключить из обработки: минимизированные файлы (**\*.min.js и **\*.min.css), файлы в определенных директориях (**\ckeditor\*.js, **\ckeditor\**\*.js, **\ckeditor\*.css и **\ckeditor\**\*.css) и конкретные файлы (**\MicrosoftAjax.js).
    С помощью атрибутов JsSourceFiles и CssSourceFiles элемента AjaxMin связываются группы файлов, которые были объявлены внутри тегов ItemGroup, с конкретными обработчиками. В атрибутах JsSourceExtensionPattern и CssSourceExtensionPattern указаны расширения исходных файлов, а атрибутах JsTargetExtension и CssTargetExtension – расширения файлов, которые появятся на выходе. В остальных атрибутах элемента AjaxMin указываются настройки алгоритмов сжатия, о которых я расскажу чуть позже.

  4. Щелкните по проекту правой кнопкой мыши и в контекстном меню выберите пункт Reload Project. После чего проект загрузиться в Visual Studio и его пиктограмма перестанет быть серой.

После проделанных выше действий можно запустить сборку проекта. Если сборка прошла успешно, то чтобы увидеть минимизированные файлы в меню проекта отметьте кнопку Show All Files.

Кнопка Show All Files
Рис. 2. Кнопка Show All Files в меню проекта

Теперь минимизированные файлы станут видны в Solution Explorer:

Минимизированные файлы
Рис. 3. Минимизированные файлы

Эти файлы не включены в проект. Если вы публикуете свои проекты с помощью средств развертывания Visual Studio, то вам следует включить данные файлы в проект, иначе они не будут скопированы при развертывании. Но из-за включения файлов в проект могут возникнуть проблемы с системой контроля версий: перед сборкой придется брать все файлы на Check Out, чтобы Ajax Minifier смог их перезаписать.

Рассмотрим более подробно атрибуты элемента AjaxMin, отвечающие за настройку алгоритма минимизации JS-файлов:

  1. JsCollapseToLiteral="(true|false)". Разрешает преобразование конструкций вида: new Object() в {} и new Array() в [].
  2. JsCombineDuplicateLiterals="(true|false)". Разрешает замену повторяющихся литералов на локальные переменные. Приведу пример:
    function foo(a) 
    {    
        a.b = 12345;    
        a.c = 12345;    
        a.d = 12345;    
    }
    

    В процессе минимизации заменяется на:

    function foo(a)    
    {    
        var b=12345;    
        a.b=b;    
        a.c=b;    
        a.d=b;    
    }
    
  3. JsEvalTreatment="(MakeAllSafe|Ignore|MakeImmediateSafe)". Позволяет указать, как переименовывать локальные переменные и функции, используемые в коде, который выполняется с помощью функции eval. По умолчанию используется опция Ignore, которая не переименовывает переменные внутри выражения, исполняемого eval, но переименовывает переменные вне eval, что может привести к ошибкам. Опция MakeImmediateSafe запрещает переименовывать переменные, находящиеся в одной области видимости с вызовами eval. Опция MakeAllSafe не только запрещает переименовывать переменные, находящиеся в одной области видимости с вызовами eval, но и запрещает переименование в родительских областях. В своих проектах я стараюсь отказаться от использования eval и производить максимальное сжатие с помощью опции Ignore.
  4. JsIndentSize="(число)". Используется для указания количества пробелов для добавления отступов во вложенных конструкциях, если используется многострочный режим вывода (в случае, когда значение атрибута JsOutputMode равно MultipleLines).
  5. JsLocalRenaming="(CrunchAll|KeepAll|KeepLocalizationVars)". Определяет варианты переименования локальных переменных и функций. По умолчанию используется опция KeepAll, которая запрещает переименовывать локальные переменные и функции. Опция CrunchAll наоборот разрешает переименование. Опция KeepLocalizationVars запрещает только переименование переменных, имя которых начинается с префикса L_ (могут применяться для локализации (перевода на другие языки) приложения).
  6. JsMacSafariQuirks="(true|false)". Включает поддержку особенностей движка JavaScript браузера Safari для Mac.
  7. JsOutputMode="(SingleLine|MultipleLines)". При выбранной опции SingleLine (значение по умолчанию) весь код минимизируется в одну строку. Опция MultipleLines позволяет сохранить в коде переводы строк (для удобства чтения).
  8. JsRemoveUnneededCode="(true|false)". Удаляет код, который не используется в процессе выполнения скрипта.
  9. JsStripDebugStatements="(true|false)" />. Удаляет отладочный код (директивы debugger, вызовы методов из следующих пространств имен: $Debug, Debug, Web.Debug, Msn.Debug, а также вызовы функции WAssert).

Теперь рассмотрим атрибуты, отвечающие за настройку алгоритма минимизации CSS-файлов:

  1. CssCommentMode="(None|All|Hacks)". Указывает, как обрабатывать комментарии в CSS-коде. По умолчанию используется опция None, которая разрешает удаление всех комментариев за исключением «важных» (комментарии вида: /*! какой-нибудь текст */). Опция All разрешает удаление всех комментариев. Опция Hacks оставляет в коде комментарии, которые используются в качестве CSS-хаков, и «важные» комментарии.
  2. CssSeverity="(число)". Задает уровень серьезности ошибок, выдаваемых в процессе минимизации. По умолчанию уровень задан равным нулю (выводятся только синтаксические ошибки). Если нужно получить более подробную информацию (возможные ошибки, предупреждения и советы), то следует поднять значение уровня серьезности.
  3. CssTermSemicolons="(true|false)". Если установлено значение true, то после каждого стилевого правила ставится точка с запятой. По умолчанию установлено значение false.
  4. CssColorNames="(Strict|Hex|Major)". По умолчанию используется опция Strict, которая разрешает заменять RGB-коды цветов на «строгие» названия цветов (одобренные W3C), если эти названия короче RGB-кодов. При установленной опции Hex, все названия цветов заменяются на RGB-коды. Опция Major разрешает использование названий цветов, которые поддерживаются всеми основными браузерами (рекомендации W3C игнорируются).
  5. CssExpandOutput="(true|false)". Если выбрано значение true, то в коде будут сохранены переносы строк. В противном случае, код минимизируется в одну строку (значение по умолчанию).
  6. CssIndentSpaces="(число)". Если значение атрибута CssExpandOutput установлено в true, то это свойство задает количество пробелов для добавления отступов во вложенных конструкциях (по умолчанию равно 4).

Таким образом, в Ajax Minifier можно гибко управлять алгоритмом сжатия и получать на выходе ожидаемый результат.

Minifier

Страница VS-расширения Minifier на сайте Visual Studio Gallery

Для Visual Studio также существует альтернатива утилите Ajax Minifier – это расширение Minifier. Данное расширение можно загрузить с сайта Visual Studio Gallery. Minifier использует для минимизации файлов сторонние утилиты: JavaScript-код сжимается с помощью Google Closure Compiler, а CSS-код — YUI Compressor.

Для того, чтобы минимизировать JS-файл с помощью Minifier, нужно сначала выделить данный файл в Solution Explorer, а затем в окне Properties задать свойству Custom Tool значение равное GoogleClosureCompiler и нажать на клавиатуре Enter, после чего будет сгенерирована минимизированная версия файла.

Настройка сжатия JS-файла с помощью VS-расширения Minifier
Рис. 4a. Настройка сжатия JS-файла с помощью Minifier

Минимизация CSS-файлов производится аналогичным способом, только свойству Custom Tool нужно задать значение равное YUICompressor.

Настройка сжатия CSS-файла с помощью VS-расширения Minifier
Рис. 4б. Настройка сжатия CSS-файла с помощью Minifier

Minifier производит минимизацию файла сразу после его сохранения. Поэтому при сохранении файла будет чувствоваться некоторая задержка (особенно при сохранении JS-файлов, т.к. минимизация производится с помощью веб-сервиса Closure Compiler). Преимущество данного подхода заключается в том, что минимизированные файлы сразу включаются в проект, благодаря чему решаются проблемы, возникающие при работе с системами контроля версий и развертывании. Недостатками данного VS-расширения являются: отсутствие сообщений об ошибках и возможности настроить алгоритм сжатия файла.

Заключение

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

<link href="@Url.Content("~/Content/basic.min.css")" rel="stylesheet" 
    type="text/css" />
...
<script src="@Url.Content("~/Areas/Admin/Scripts/LeftEast/Common.min.js")" 
    type="text/javascript"></script>

Но это достаточно рутинная работа. Кроме того, для отладочной и производственной версий сайта нам требуются разные версии файлов (исходные файлы нужны в отладочной версии, а минимизированные в производственной).
Перечисленные выше проблемы помогут решить менеджеры ресурсов (asset managers). Кроме того, многие менеджеры ресурсов предоставляют очень важную для клиентской оптимизации функциональность — объединение JS- и CSS-файлов. В следующей статье я расскажу о менеджерах ресурсов для ASP.NET MVC и подробно рассмотрю возможности одного из них.

Ссылки

  1. Best Practices for Speeding Up Your Web Site
  2. Документация к Microsoft Ajax Minifier на официальном сайте ASP.NET
  3. Документация к Microsoft Ajax Minifier на сайте CodePlex
  4. Страница VS-расширения Minifier на сайте Visual Studio Gallery

UPD: Добавил UglifyJS в список утилит минимизации JS-кода.

Реклама
  1. Grey
    23.08.2013 в 16:02

    спасибо 🙂

  1. No trackbacks yet.

Добавить комментарий

Заполните поля или щелкните по значку, чтобы оставить свой комментарий:

Логотип WordPress.com

Для комментария используется ваша учётная запись WordPress.com. Выход / Изменить )

Фотография Twitter

Для комментария используется ваша учётная запись Twitter. Выход / Изменить )

Фотография Facebook

Для комментария используется ваша учётная запись Facebook. Выход / Изменить )

Google+ photo

Для комментария используется ваша учётная запись Google+. Выход / Изменить )

Connecting to %s

%d такие блоггеры, как: