Минификация CSS и Javascript в Website проекте ASP.NET

Про минимизацию javascript и css знают все. Ну если кто не знает, то, вкратце, это уменьшение объема файлов за счет удаления комментариев, разметки, переносов строк и прочего. Особенно акуально оно для интернет сайтов, которые при первом же визите пользователя должны порадовать его своей производительностью. Но наш проект, во-первых, работает в локальной сети, а во-вторых, используется на одних и тех же компьютерах изо дня в день, поэтому мы долгое время совсем не задумывались об оптимизации скриптов и стилей. Пока не стали плотно работать с ExtJS.

Созданная страничка («Расчетный документ» = сокращенно РД) со всеми подключенными скриптами и стилями весит больше 5 Мб (около 200 файлов). Одно только сгенерированное DOM-дерево в коде HTML содержит более 500 000 байт. Работать с системой пользователь может начать не раньше, чем через 5 секунд после загрузки страницы (инициализация скриптов, ExtJS  и т.п.).

Как оказалось, несмотря на наличие локальной сети у заказчика и частую работу с одними и теми же страницами (должно же быть встроенное кэширование в браузере), иногда с загрузкой страниц возникают проблемы. Поэтому было решено уменьшить количество запросов к серверу и поработать над общей производительностью ExtJS в IE8.Для минификации скриптов сначала скачал Google Closure Compiler, как один из самых популярных, да и корпорация добра плохого не сделает…

Google Closure Compiler

Положил файлик compressor.jar в папку со скриптами. Чтобы его запускать, пришлось поставить java. Сделал bat-файлик запуска с двумя командами, последняя из которых всего лишь делает задержку в 10 секунд, чтобы можно было успеть увидеть возможные ошибки и самому закрыть окно командной строки. Вот таким вот образом слил все скрипты в один файл, вес уменьшился примерно в полтора раза (это содержимое bat-файла):

cd %0\..\
java -jar compiler.jar --js=NavigationJS.js --js=SSSC.js (здесь еще куча файликов аналогичным образом)  --js_output_file=bcr_master.min.js
ping -n 1 -w 100000 192.168.254.254 >nul

Все просто и элегантно, провозился минут пять. Команда cd %0\..\ в начале файла нужна для того, чтобы дальнейшее выполнение команд происходило внутри директории, в которой расположен сам .bat файл, а не с директории по умолчанию после запуска командной строки.

Но тут возникла идея минимизировать не только JS, но и CSS файлы, а Google Closure этого делать, увы, не умеет. Поэтому решил переделать все на YUI Compressor, о котором много отзывов, и он также сжимает CSS.

YUI Compressor

Для объединения всех файлов проекта был написан батник уже значительно большего объема, потому что этот YUI не умеет воспринимать в строке сразу несколько файлов — ему можно скармливать только по одному файлу. А значит, предварительно нам нужно слепить файлы в один временный файл командой copy, а уже потом его сжать юай компрессором.

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

cd %0\..\
copy /b .\..\..\Scripts\jquery\autoresize.jquery.min.js + .\..\..\Scripts\jquery\jquery.nano.js + .\..\..\Scripts\jquery\jqueryslidemenu.js + .\..\..\Scripts\UnTree\Tabs.js + .\..\..\Scripts\FixedHeader.js + .\NavigationJS.js + .\SSSC.js + .\Filters.js + .\Dictionaries.js + .\EditResurce.js + .\Calc.js + .\Formula.js + .\LoadExcel.js + .\GetNSBFile.js + .\SelectedTableColumns.js + .\LoadMNT.js + .\LoadNSB.js + .\GetResInXLS.js + .\WarningUnConfMNT.js + .\Condor.js + .\Bids.js + .\SO.js combined.js
java -jar yuicompressor-2.4.8.jar combined.js --type js -o bcr_master_yui.min.js --charset cp-1251
del combined.js

copy /b .\..\..\Styles\jquery\jqueryslidemenubcr.css + .\..\..\BCR\Styles\TableBCR.css + .\..\..\BCR\Styles\Kondor.css combined.css
java -jar yuicompressor-2.4.8.jar combined.css -o .\..\..\BCR\Styles\bcr_master_yui.min.css --charset cp-1251
del combined.css

copy /b .\..\..\Scripts\JSON.js + .\..\..\Scripts\magix_expand.js + .\..\..\Scripts\collapse.js +  .\..\..\klayers.js +  .\..\..\Scripts.js +  .\..\..\Scripts\ExtFunction.js +  .\..\..\Scripts\MPMessage.js + cookies.js + Common.js + .\..\..\Scripts\jquery\jquery.nano.js +  .\..\..\Scripts\jquery\jqueryslidemenu.js + .\..\..\Scripts\UnTree\Tabs.js + NavigationJS.js + SSSC.js +  Filters.js +  Dictionaries.js + EditResurce.js +  Calc.js + LoadExcel.js + GetNSBFile.js + SelectedTableColumns.js + LoadMNT.js + LoadNSB.js + GetResInXLS.js + WarningUnConfMNT.js + Condor.js +  SO.js +  .\..\..\Scripts\FixedHeader.js + Formula.js + Bids.js combined.js
java -jar yuicompressor-2.4.8.jar combined.js --type js -o combined.min.js --charset cp-1251
copy /b combined.min.js + .\..\..\Scripts\jquery\jquery.blockUI.min.js + .\..\..\Scripts\jquery\jquery.cluetip.min.js + .\..\..\Scripts\jquery\jquery-tooltip\jquery.tooltip.min.js + .\..\..\Scripts\jquery\jquery-tooltip\lib\jquery.bgiframe.min.js + .\..\..\Scripts\jquery\jquery-contextmenu\jquery.contextmenu.min.js +  .\..\..\Scripts\jquery\jquery.AddIncSearch.min.js +  .\..\..\Scripts\jquery\jquery-ui-timepicker-addon.min.js + .\..\..\Scripts\jquery\autoresize.jquery.min.js + .\..\..\Scripts\jquery\plugins.fileupload.min.js mbcrfull.min.js
del combined.min.js
del combined.js

copy /b .\..\..\Scripts\ext.js\ext-all-debug.js + .\..\..\Scripts\ext.js\locale\ext-lang-ru.js + .\..\..\Scripts\jquery\jqueryslidemenu.js + OM.js + History.Ext.js + RD.Ext.js + RD.js combined.js
java -jar yuicompressor-2.4.8.jar combined.js --type js -o page_RD.min.js --charset cp-1251
del combined.js

::copy /b .\..\..\Styles\MPMessage.css + .\..\..\BCR\Styles\TableBCR.css + .\..\..\BCR\Styles\Kondor.css combined1.css
::java -jar yuicompressor-2.4.8.jar combined1.css -o .\..\..\BCR\Styles\mbcrfull.min.css --charset cp-1251
::del combined1.css

::copy /b .\..\..\Styles\jquery\ui.all.css + .\..\..\Styles\jquery\jquery.cluetip.css + .\..\..\Styles\jquery\jqueryslidemenubcr.css + .\..\..\Styles\jquery\jquery.MultiFile.css + .\..\..\Styles\jquery\jqueryslidemenu.css combined2.css
::java -jar yuicompressor-2.4.8.jar combined2.css -o .\..\..\Styles\jquery\mbcrfull.min.css --charset cp-1251
::del combined2.css

::copy /b .\..\..\Styles\jquery\jquery.MultiFile.css + .\..\..\Styles\Tree.css + .\..\..\BCR\Styles\OM.css + .\..\..\Styles\tooltip.css + .\..\..\Styles\jquery\jqueryslidemenu.css + .\..\..\BCR\Styles\RD.css combined.css
::java -jar yuicompressor-2.4.8.jar combined.css -o .\..\..\BCR\Styles\page_RD.min.css --charset cp-1251
::del combined.css

::ping -n 1 -w 100000 192.168.254.254 >nul

Так выглядит файл запуска компрессора в настоящее время. Как видите, строки минификации CSS закомментированы в силу специфики нашего проекта. На самом деле в минификации CSS есть масса тонкостей, а сливать их а один файл — задача не из легких:

  • Во-первых, нужно тщательно следить за использованием относительных путей к изображениям. Стили ExtJS и других библиотек основаны на относительных путях, а значит они сразу выпадают из нашего объединения (кстати, в большинстве своем они уже минифицированы).
  • Во-вторых, в CSS файлах могут встречать команды @import. Это ад.
  • В третьих, можно же создавать минифицированные объединенные стили отдельно в каждой из папок, а потом включать в проект, но, как выяснилось, таких файлов у нас получается не так уж много и прирост производительности будет небольшой. Поэтому решили минификацию CSS пока отложить. (Можно было и на Google Closure Compiler остановиться в таком случае)

А теперь самое интересное — батник у нас есть, объединенные скрипты генерируются успешно, осталось их прописать в релизной версии проекта. Все вроде работает, нужно коммитить…. каждый раз запуская скрипт.

Автоматизация минификации при коммите в релиз

Дело в том, что у нас две ветки в SVN — рабочая (DEV) и релизная. Каждый раз перед выкладыванием версии на сервер мы выполняем merge всех (ну или нужной части) изменений, билдим и только потом (если все нормально) коммитим. И надо, во-первых, не забывать самому постоянно выполнять этот батник перед коммитом в релиз, а во-вторых, следить за всеми разработчиками, чтобы тоже не забывали это делать.

И тут нам на помощь приходят Hook Scripts в TortoiseSVN!

В настройках клиента SVN заходим на вкладку Hook Scripts и выбираем там папку нашего проекта и сам батник. В опциях Hook Type выбираем «Start-Commit Hook«, чтобы скрипт запускался перед тем, как отобразится окно коммита.

Настройа запуска минификации перед коммитом

Настройка запуска минификации перед коммитом

Вуаля, все работает и не надо ни за кем следить! 🙂

Есть, конечно, куча встроенных в visual studio средств объединения и минификации, но их тоже надо устанавливать на каждой машине, а еще в них тоже много тонкостей. До этого у нас использовался встроенный метод Composite Scripts, но он не обладает достаточной функциональностью в сравнении с теми же YUI или Google closure Compiler.

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

Ваш e-mail не будет опубликован. Обязательные поля помечены *