Со времён MSIE4 и блокнота, Я люблю комментировать написанный код. Но одно дело, когда проект создаётся для себя, или при компиляции комментарии не попадут конечному пользователю. И совсем другое дело, когда написанные комментарии могут попасть конечному клиенту. Во первых, они ему не нужны, а во вторых, в комментариях может содержаться некий текст, который может подвергнуть опасности этот или другой проект. И в третьих, содержимые в клиентском коде (HTML, JS, CSS) комментарии, даже в сжатом виде, создают паразитный трафик.
Изучая такой код иногда можно натолкнуться на интересные вещи. Вот, к примеру, один из кусков комментариев на одном всеми известном сайте:
Я хочу рассказать о нескольких ситуациях, с которыми мне приходится сталкиваться достаточно часто:

(Project Properties → Build Events → {Pre-build event command line/Post-build event command line})
Для того, чтобы работал этот метод, необходимо прописать команду копирования наших клиентских файлов во временный файл. За это отвечает команда копирования:
Не забудьте включить событие post-build только после успешной сборки:
В данном примере есть одно ограничение. Если необходимо отладить клиентский код, то необходимо будет вручную убрать код копирования и сжимания js файла. Или захардкодить для отладочного примера ссылку на оригинальный файл скрипта, а не на сжатый файл ресурса из сборки. Это связано с тем, что Build Events выполняется во всех конфигурациях.
Ещё можно написать сервис и мониторить изменение файлов на жёстких дисках серверов и сжимать их при изменении. Но такое решение неудобно по 2 причинам:
Т.к., в большинстве случаев, я выкладываю проект через команду «Publish Web» (т.е. имеется физический доступ к серверу), в студии, то Я решил копать в эту сторону и попытаться сжимать файлы сразу после выкладывания проекта на рабочий сервер.
Вот тут я столкнулся с первой проблемой: Для события публикации в студии нет стандартного UI.
Немного погуглив, необходимое событие нашлось в EnvDTE для студии, которое используется для написания Add-In’ов.
Итак, написав небольшой Add-In Я добавил в него необходимые команды и радовался жизни. Для примера, команда на сжатие файла Style.css выглядела следующим образом:
Пришлось копать дальше… В документации на разработку Ad-In’ов для студии нет ни одного упоминания о пути куда выкладывалось решение. В результате, самым прямолинейным путём осталось копать память. К моему облегчению, код публикации написан на управляемом коде, так что использование рефлексии помогло найти место переменной для VS 2005 и 2008 быстро. Для VS 2010 место переменной, где хранится путь к папке, куда выкладывалось решение пришлось поискать подольше, т.к. разработчики добавили профили выкладывания (Publish Profile).
В результате, получился Add-In позволяющий выполнять произвольные команды перед и после выкладывания проекта.
Прописанные комманды сохраняются в .sln файле решения, так что все разработчики решения у которых будет стоять этот Add-In смогут выложить решение с применением необходимого сжатия.
Проблемы:
При использовании TFS 2010 есть проблема (ссылка больше не доступна). Соответственно, при использовании VS9 и VS10, при открытии решения любым разработчиком, sln файл будет взят разработчиком на изменение автоматически.
Если Вы пользуетесь VSS или Tortoise, то таких проблем нет.
Есть ещё одна проблема связанная с TFS, которую Я пока до конца не изучил: Если .sln файл взят на изменение другим разработчиком, то плагин не может прочитать команды для сжатия ��з sln файла.
Изучая такой код иногда можно натолкнуться на интересные вещи. Вот, к примеру, один из кусков комментариев на одном всеми известном сайте:
… //ubepua07/default/main/SonyStyle/WORKAREA/Common/SonyStyleStorefrontAssetStore/include/CMSpot …А внизу всего этого хозяйства красуется вот такой текст:
… (Developers should log their changes, including line number for reference here) …Вот пример комментариев от компании Яндекс:
"Любишь заглядывать в консоль? А может и js умеешь писать?…"
Я хочу рассказать о нескольких ситуациях, с которыми мне приходится сталкиваться достаточно часто:
- Клиентский код в виде ресурсов в сборке
- Клиентский код в качестве файлов WebForms или MVC решения
- Клиентский код, находящийся на сайте для статичных ресурсов (http://static.sitename.com)
Клиентский код в виде ресурсов в сборке.
Я начал искать варианты решения задачи используя подручные средства. И для сжатия ресурсов внутри сборки решение нашлось достаточно быстро. На идею про сжатие скрипта в сборке меня натолкнул скрипт, который я использовал до появления в 10ой студии опции, с видоизменением файлов Web.config для разных конфиураций выкладывания решения (Идея была не моя и автора идеи Я забыл). В Visual Studio можно написать команду выполняемую до и после сборки проекта.
(Project Properties → Build Events → {Pre-build event command line/Post-build event command line})
Для того, чтобы работал этот метод, необходимо прописать команду копирования наших клиентских файлов во временный файл. За это отвечает команда копирования:
copy "$(ProjectDir)Script.js" "$(ProjectDir)Script.orig.js" /V /YЗатем, необходимо применить сжатие для файла скрипта. В данном проекте, программа для сжатия находится в корневой папке:
"$(SolutionDir)..\..\SourceCruncher.exe" /IO:"$(ProjectDir)Script.js" /YПосле того, как сборка собралась, необходимо вернуть наш клиентский скрипт в оригинальный вид. Для этого, добавляем в событие «Post-build event command line:» код перемещения сжатого варианта файла на не сжатый вариант:
move /Y "$(ProjectDir)Script.orig.js" "$(ProjectDir)Script.js"Важно!
Не забудьте включить событие post-build только после успешной сборки:
Run the post-build event: On successful build.Иначе, при ошибки компиляции файл Script.js останется сжатым.
В данном примере есть одно ограничение. Если необходимо отладить клиентский код, то необходимо будет вручную убрать код копирования и сжимания js файла. Или захардкодить для отладочного примера ссылку на оригинальный файл скрипта, а не на сжатый файл ресурса из сборки. Это связано с тем, что Build Events выполняется во всех конфигурациях.
Клиентский код в качестве файлов WebForms или MVC решения
Из готовых решений, Я нашёл только один вариант, сжимать клиентский код в рантайме через регулярное выражение. С одной стороны, решение очень простое, но Я лучше потрачу ресурсы процессора на более интересную задачу, нежели сжатие одного и того-же файла при каждом запросе.Ещё можно написать сервис и мониторить изменение файлов на жёстких дисках серверов и сжимать их при изменении. Но такое решение неудобно по 2 причинам:
- Физического доступа на сервер может не быть.
- Действие происходит не явно.
Т.к., в большинстве случаев, я выкладываю проект через команду «Publish Web» (т.е. имеется физический доступ к серверу), в студии, то Я решил копать в эту сторону и попытаться сжимать файлы сразу после выкладывания проекта на рабочий сервер.
Вот тут я столкнулся с первой проблемой: Для события публикации в студии нет стандартного UI.
Немного погуглив, необходимое событие нашлось в EnvDTE для студии, которое используется для написания Add-In’ов.
Итак, написав небольшой Add-In Я добавил в него необходимые команды и радовался жизни. Для примера, команда на сжатие файла Style.css выглядела следующим образом:
"$(SolutionDir)\Solution Items\SourceCruncher.exe" /IO:"\\server\Path\styles\Style.css" /YВ начале, проблемы не было, т.к. каждый проект выкладывался только в одну папку и путь к папке можно было захардкодить. Но если необходима бесперебойная работа сервера, то отключать пользователей сообщением App_Offile.html, Я считаю не вполне этично. Так что сначала начал выкладывать проекты в 2 папки. По прин��ипу, рабочая версия/предыдущая версия. Сначала Я решил эту проблему таким образом:
- Подлключаюсь к IIS через ADSI
- Беру путь папки в которой у меня работает решение
- Удаляю всё содержимое папки {ProjectName}_PrevVersion
- Копирую всё решение из этой папки в папку {ProjectName}_PrevVersion
- Переключаю решение с рабочей папки на папку {ProjectName}_PrevVersion
- Выкладываю решение в рабочую папку
- Переключаю папки с {ProjectName}_PrevVersion на рабочую папку.
Пришлось копать дальше… В документации на разработку Ad-In’ов для студии нет ни одного упоминания о пути куда выкладывалось решение. В результате, самым прямолинейным путём осталось копать память. К моему облегчению, код публикации написан на управляемом коде, так что использование рефлексии помогло найти место переменной для VS 2005 и 2008 быстро. Для VS 2010 место переменной, где хранится путь к папке, куда выкладывалось решение пришлось поискать подольше, т.к. разработчики добавили профили выкладывания (Publish Profile).
В результате, получился Add-In позволяющий выполнять произвольные команды перед и после выкладывания проекта.
Установка Add-In'а
(Пример для Visual Studio 2008, но от VS 2010 никаких различий нет.)
Элемент меню AddIn'а

Запуск окна с надстройкой:

К сожалению, вариант установки есть только один — ручками. Инсталлера, увы, нет.:
Элемент меню AddIn'а

Запуск окна с надстройкой:

К сожалению, вариант установки есть только один — ручками. Инсталлера, увы, нет.:
- Скачиваем весь архив и распаковываем его в желаемую папку (Или в известную папку с Add-in’ами, если у Вас такая уже есть).
- Затем, открываем студию: Tools → Options → Add-in/Macros Security и в окне Add-in File Paths прописываете путь который Вы указали на шаге один. (Или переносите распакованные файлы в папку указанную в этом списке).

- Перезапускаем студию, включаем Add-In через «Add-in Manager» (Tools→Add-in Manager...→Flatbed.EnvDTE)

- И в меню Tools появится команда «Flatbed.EnvDTE Properties».

- После этого, загружаем проект
- Если разработчиков проекта несколько человек, то добавляем в проект консольное приложение для сжатия кода (В данном случае, в качестве примера, используется моя утилита SourceCruncher). Или указываем сетевой путь до консольного приложения, которое будет доступно всем разработчикам.
- Открываем окно настройки плагина «PluginDTE.CmdLine» (Tools→Flatbed.EnvDTE Properties→PluginDTE.CmdLine)
- И прописываем консольную команду на сжимание интересующих файлов. Пример:
"\\server\Path\SourceCruncher.exe" /IO:"${PublishLocation}js\Utils.js" /Y - После того, как команды прописаны, нажимаем Ctrl+S для сохранения команд. (Команды сохраняются в .sln файле решения, так что все разработчики проекта у которых будет стоять этот Add-In увидят все внесённые изменения).
Тут есть один минус, если проект лежит в TFS’е, то каждый раз при открытии проекта файл .sln будет браться на редактирование (Check out).
Прописанные комманды сохраняются в .sln файле решения, так что все разработчики решения у которых будет стоять этот Add-In смогут выложить решение с применением необходимого сжатия.
Проблемы:
При использовании TFS 2010 есть проблема (ссылка больше не доступна). Соответственно, при использовании VS9 и VS10, при открытии решения любым разработчиком, sln файл будет взят разработчиком на изменение автоматически.
Если Вы пользуетесь VSS или Tortoise, то таких проблем нет.
Есть ещё одна проблема связанная с TFS, которую Я пока до конца не изучил: Если .sln файл взят на изменение другим разработчиком, то плагин не может прочитать команды для сжатия ��з sln файла.
Пример на WebForms
Приведу пример ситуации, при котором генерируемый код вёрстки получается сильно замусоренный лишним текстом (Для примера Я использовал типизированный Repeater, написанный Andrey Shchekin в 2007 году):<user:TypedRepeater ID="repFilters" DataItemTypeName="Company.Dal.Catalog.FilterToc" OnItemDataBound="repFilters_ItemDataBound" runat="server">
<ItemTemplate>
<tr>
<td><%# Container.DataItem.FilterName %></td>
<td>
<%-- Строковый тип фильтра--%>
<user:TypedRepeater ID="repFiltersString" DataItemTypeName="Company.Dal.Catalog.FilterString" runat="server">
<ItemTemplate>
<asp:TextBox Text="<%# Container.DataItem.FilterValue %>" runat="server" />
</ItemTemplate>
</user:TypedRepeater>
<%-- Битовый тип фильтра --%>
<user:TypedRepeater ID="repFiltersBit" DataItemTypeName="Company.Dal.Catalog.FilterBit" runat="server">
<ItemTemplate>
<asp:CheckBox Checked="<%# Container.DataItem.FilterValue %>" Text="<%# Container.DataItem.FilterName %>" runat="server" />
</ItemTemplate>
</user:TypedRepeater>
…
</td>
</tr>
</ItemTemplate>
</user:TypedRepeater>Если представить что типов фильтров может быть, скажем, 10, так же как и среднее кол-во фильтров на страницу, то в файле отправляемом клиенту будет большое кол-во паразитного траффика.Клиентский код, находящийся на сайте для статичных ресурсов
Есть несколько решений, которые используют одни и те-же стили. Допустим, основной сайт и интранет сайт одной из задач которого является написание статей для основного сайта в WYSIWYG редакторе. Соответственно, базовые стили обоих сайтов должны быть идентичными. Для этого используются файлы стилей и изображений доступным обоим сайтам (это статья не о версионности, так что проблему соответствия всех сайтов одним глобальным стилям Я опускаю. Так же как и проблему хранения версий). Для примера приведу несколько вариантов:- CSS/JS файлы находятся внутри основного решения в виде ссылок на актуальные файлы на общем сервере.
В таком случае можно применять к ним сжатие как и к файлам основного решения описанного в пп.2 статьи. - CSS/JS файлы находятся в основном проекте и при выкладывании основного решения перемещаются на общий сервер.
- Если верстальщик не пользуется VS, а использует свой инструмент для редактирования общих файлов, то можно использовать программу DropTarget в совокупности с программой для сжатия клиентского кода.
