Со времён 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 в совокупности с программой для сжатия клиентского кода.