- PVSM.RU - https://www.pvsm.ru -
Со времён MSIE4 и блокнота, Я люблю комментировать написанный код. Но одно дело, когда проект создаётся для себя, или при компиляции комментарии не попадут конечному пользователю. И совсем другое дело, когда написанные комментарии могут попасть конечному клиенту. Во первых, они ему не нужны, а во вторых, в комментариях может содержаться некий текст, который может подвергнуть опасности этот или другой проект. И в третьих, содержимые в клиентском коде (HTML, JS, CSS) комментарии, даже в сжатом виде, создают паразитный трафик.
Изучая такой код иногда можно натолкнуться на интересные вещи. Вот, к примеру, один из кусков комментариев на одном всеми известном сайте:
… //ubepua07/default/main/SonyStyle/WORKAREA/Common/SonyStyleStorefrontAssetStore/include/CMSpot …
А внизу всего этого хозяйства красуется вот такой текст:
… (Developers should log their changes, including line number for reference here) …
Вот пример комментариев от компании Яндекс:
"Любишь заглядывать в консоль? А может и js умеешь писать?…"
Я хочу рассказать о нескольких ситуациях, с которыми мне приходится сталкиваться достаточно часто:
В тексте статьи Я использую самописный инструмент: SourceCruncher [1]. Есть намного более эффективные средства для сжатия кода вёрстки HTML, CSS и JS скриптов с поддержкой затенения и более эффективного процесса сжатия. (Я написал свой вариант для более детального понимания процесса парсинга браузерами кода вёрстки и скриптов.)
Я начал искать варианты решения задачи используя подручные средства. И для сжатия ресурсов внутри сборки решение нашлось достаточно быстро. На идею про сжатие скрипта в сборке меня натолкнул скрипт, который я использовал до появления в 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 выполняется во всех конфигурациях.
Из готовых решений, Я нашёл только один вариант, сжимать клиентский код в рантайме через регулярное выражение. С одной стороны, решение очень простое, но Я лучше потрачу ресурсы процессора на более интересную задачу, нежели сжатие одного и того-же файла при каждом запросе.
Ещё можно написать сервис и мониторить изменение файлов на жёстких дисках серверов и сжимать их при изменении. Но такое решение неудобно по 2 причинам:
Пришлось искать решение в режиме «До выкладывания». Сначала Я решил делать это через события «Build Events» как и в случае с кодом ресурсов сборки. Но они происходят каждый раз после сборки проекта, а не перед выкладыванием. Во первых, затормаживая сам процесс сборки (особенно если учесть что клиентского кода может быть очень много), а во вторых, усложняет процесс поиска ошибок в клиентском коде.
Т.к., в большинстве случаев, я выкладываю проект через команду «Publish Web» (т.е. имеется физический доступ к серверу), в студии, то Я решил копать в эту сторону и попытаться сжимать файлы сразу после выкладывания проекта на рабочий сервер.
Вот тут я столкнулся с первой проблемой: Для события публикации в студии нет стандартного UI.
Немного погуглив, необходимое событие нашлось в EnvDTE для студии [2], которое используется для написания Add-In’ов.
Итак, написав небольшой Add-In Я добавил в него необходимые команды и радовался жизни. Для примера, команда на сжатие файла Style.css выглядела следующим образом:
"$(SolutionDir)Solution ItemsSourceCruncher.exe" /IO:"\serverPathstylesStyle.css" /Y
В начале, проблемы не было, т.к. каждый проект выкладывался только в одну папку и путь к папке можно было захардкодить. Но если необходима бесперебойная работа сервера, то отключать пользователей сообщением App_Offile.html, Я считаю не вполне этично. Так что сначала начал выкладывать проекты в 2 папки. По принципу, рабочая версия/предыдущая версия. Сначала Я решил эту проблему таким образом:
Но такой подход занимает много времени, несмотря на то, что всё это решается автоматом. А если ещё весь проект лежит на DFS, то сервера будут заниматься лишней работой перетасовывания файлов.
Пришлось копать дальше… В документации на разработку Ad-In’ов для студии нет ни одного упоминания о пути куда выкладывалось решение. В результате, самым прямолинейным путём осталось копать память. К моему облегчению, код публикации написан на управляемом коде, так что использование рефлексии помогло найти место переменной для VS 2005 и 2008 быстро. Для VS 2010 место переменной, где хранится путь к папке, куда выкладывалось решение пришлось поискать подольше, т.к. разработчики добавили профили выкладывания (Publish Profile).
В результате, получился Add-In позволяющий выполнять произвольные команды перед и после выкладывания проекта.
"\serverPathSourceCruncher.exe" /IO:"${PublishLocation}jsUtils.js" /Y
Прописанные комманды сохраняются в .sln файле решения, так что все разработчики решения у которых будет стоять этот Add-In смогут выложить решение с применением необходимого сжатия.
Проблемы:
При использовании TFS 2010 есть проблема [4] (ссылка больше не доступна). Соответственно, при использовании VS9 и VS10, при открытии решения любым разработчиком, sln файл будет взят разработчиком на изменение автоматически.
Если Вы пользуетесь VSS или Tortoise, то таких проблем нет.
Есть ещё одна проблема связанная с TFS, которую Я пока до конца не изучил: Если .sln файл взят на изменение другим разработчиком, то плагин не может прочитать команды для сжатия из sln файла.
Приведу пример ситуации, при котором генерируемый код вёрстки получается сильно замусоренный лишним текстом (Для примера Я использовал типизированный Repeater [5], написанный 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:HyperLink NavigateUrl="<%# PageGetFilterUrl(Container.DataItem) %>"
Text="<%# Container.DataItem.FilterValue %>" runat="server"/>
</ItemTemplate>
</user:TypedRepeater>
<%-- Битовый тип фильтра --%>
<user:TypedRepeater ID="repFiltersBit" DataItemTypeName="Company.Dal.Catalog.FilterBit" runat="server">
<ItemTemplate>
<asp:HyperLink NavigateUrl="<%# PageGetFilterUrl(Container.DataItem) %>"
Text="<%# Container.DataItem.FilterValue %>" runat="server"/>
</ItemTemplate>
</user:TypedRepeater>
…
</td>
</tr>
</ItemTemplate>
</user:TypedRepeater>
Немного комментариев: Есть 2 датасета. Первый это типы фильтров, а 2ой значения фильтров. Код в DataBind’е выглядит так:
repFilters.DataSource=this.GetFilters(…);
repFilters.DataBind();
А код в repFilters_ItemDataBound следующий:
…
FilterToc filter=e.Item.DataItem;
Repeater repeater;
switch(filter.FilterType)
{
case FilterType.StringFilter:
repeater=(Repeater)e.Item.FindControl("repFiltersString");
break;
case FilterType.BitFilter:
repeater=(Repeater)e.Item.FindControl("repFiltersBit");
break;
…
default:
…
}
repeater.DataSource=this.GetFilterValues(filter.FilterID);
repeater.DataBind();
…
Если представить что типов фильтров может быть, скажем, 10, так же как и среднее кол-во фильтров на страницу, то в файле отправляемом клиенту будет большое кол-во паразитного траффика.
Есть несколько решений, которые используют одни и те-же стили. Допустим, основной сайт и интранет сайт одной из задач которого является написание статей для основного сайта в WYSIWYG редакторе. Соответственно, базовые стили обоих сайтов должны быть идентичными. Для этого используются файлы стилей и изображений доступным обоим сайтам (это статья не о версионности, так что проблему соответствия всех сайтов одним глобальным стилям Я опускаю. Так же как и проблему хранения версий). Для примера приведу несколько вариантов:
тут [7]. Но т.к. проект находится на бесплатном
Автор: ZetaTetra
Источник [12]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/net/27394
Ссылки в тексте:
[1] SourceCruncher: http://www.alphaomega.somee.com/Project/Default.aspx?File=71
[2] нашлось в EnvDTE для студии: http://msdn.microsoft.com/en-us/library/envdte80.publisheventsclass.onpublishdone.aspx
[3] Скачиваем: #dl
[4] проблема: https://connect.microsoft.com/VisualStudio/feedback/details/573538/visual-studio-2010-with-tfs-2010-always-checks-out-the-solution-file-when-opening
[5] типизированный Repeater: http://www.codeproject.com/Articles/18049/A-Typed-Repeater-in-ASP-NET
[6] DropTarget: http://alphaomega.somee.com/project/Default.aspx?File=66
[7] тут: http://alphaomega.somee.com/project/Default.aspx?File=91
[8] хостинге: https://www.reg.ru/?rlink=reflink-717
[9] Visual Studio 2005 Add-In: http://yadi.sk/d/mwaD3YCr2Zypp
[10] Visual Studio 2008 Add-In: http://yadi.sk/d/j9Cv2Is42ZyrF
[11] Visual Studio 2010 Add-In: http://yadi.sk/d/x1ZdARAl2Zyrv
[12] Источник: http://habrahabr.ru/post/169529/
Нажмите здесь для печати.