English version

Интеграция стилей LESS в сайт на основе ASP.NET

Существует такая отличная штука LESS — надстройка над CSS, которая позволяет создавать стили для своего сайта более удобно, сохраняя при этом код в более аккуратном и поддерживаемом виде.

Поскольку изначально процессинг LESS написан на JavaScript, то актуальным остается вопрос каким образом интегрировать процессинг LESS-документов в проект ASP.NET. Здесь я рассмотрю известные мне способы.

JavaScript

Самый простой способ интеграции – использовать клиентский процессинг LESS. Этот способ также предлагают разработчики LESS на своем сайте.

Для подключения LESS на клиенте нужно выполнить два шага:

Шаг 1: Добавить ссылки на LESS-файлы

<link rel="stylesheet/less" type="text/css" href="styles.less" />

Шаг 2: Добавить ссылку на JavaScript-сценарий less.js с официального сайта:

<script src="less.js" type="text/javascript"></script>

В этом случае при загрузке страницы будет загружен JavaScript-сценарий для процессинга, файлы LESS, а также дополнительные файлы, если потребуется (ссылки на другие скрипты, изображения и т.д.).

Плюсы данного подхода:

Минусы данного подхода:

Выводы — данный способ подходит разве что для процесса разработки, когда LESS-код постоянно изменяется. На живый проектах использование данного способа весьма сомнительно. В моем случае минусы перекрывают все плюсы, поэтому я в своей работе не использую данный способ вообще.

Web Essentials 2012

Существует такое прекрасное расширение для Visual Studio - Web Essentials 2012. Это расширение добавляет много различных полезных вещей для веб-разработчика. Если вы не знакомы с этим расширением, очень рекомендую его посмотреть.

В контексте текущей темы, это расширение полезно с точки зрения работы с LESS. Оно может генерировать CSS-стили при сохранении LESS-файлов и при сборке проекта. Для этого после установки расширения нужно открыть окно настроек: ToolsOptionsWeb EssentialsLESS и задать соответствующие настройки.

Интеграция стилей LESS в сайт на основе ASP.NET

Теперь можно добавить LESS-файл в проект и, если вы задали настройку Show preview window, то генерация CSS будет происходить “на лету”:

Интеграция стилей LESS в сайт на основе ASP.NET

После генерации CSS-файлы также доступны в проекте:

Интеграция стилей LESS в сайт на основе ASP.NET

В целом, в таком режиме работать очень удобно – сразу видишь генерируемый код.

Но пока этот способ у меня, к сожалению, не прижился. Не уверен почему так происходит, но Web Essentials часто отказывается обрабатывать, казалось бы, корректный LESS-документ. Например, если в предыдущем примере переместить переменную в начало документа, то Web Essentials поругается на такой документ:

Интеграция стилей LESS в сайт на основе ASP.NET

Расчитывать на то, что будут обрабатываться какие-то уже готовые LESS-документы (например, LESS из Twitter Bootstrap) и вовсе не приходится, увы.

Плюсы данного подхода:

Минусы данного подхода:

Выводы – способ очень интересный и перспективный, но пока сырой. В моем случае использовать его не получается из-за постоянных ошибок процессинга.

Библиотека dotless

Библиотека dotless – это просто находка для .NET-окружения. Она предоставляет множество различных способов генерировать CSS на основе LESS в среде .NET. При этом существует вариант генерации CSS в момент выполнения приложения (есть готовый ASP.NET HTTP-handler), прямо из .NET-кода, а также при помощи консольной утилиты.

Чтобы не тратить время на генерацию CSS в момент исполнения, я предпочитаю получать CSS в момент сборки проекта. Поэтому в моем случае работает следующее решение. Сразу скажу, что в данном случае при создании проекта придется немного поработать руками – а именно подключить процессинг LESS-файлов через консольную утилиту.

Шаг 1: Создаем проект и добавляем в него LESS-файлы.

Интеграция стилей LESS в сайт на основе ASP.NET

Шаг 2: Подключаем dotless. Можно загрузить его с официального сайта, либо подключить через nuget.

Интеграция стилей LESS в сайт на основе ASP.NET

Шаг 3: Генерируем CSS при помощи dotless в момент сборки проекта.

Для генерации CSS нам придется немного поработать руками. Для этого открываем файл проекта (.csproj) и в самом конце файла, перед закрытием элемента <Project> добавляем новый Target и запускаем его после (можно перед) сборкой:

<Target Name="CompileDotlessCss" AfterTargets="AfterBuild">
  <ItemGroup>
    <__LessFiles Include="**\*.less"/>
  </ItemGroup>
  <Exec Command=""..\packages\dotless.1.3.1.0\tool\dotless.compiler.exe" -m -a "%(__LessFiles.FullPath)" "$([System.String]::Copy('%(__LessFiles.FullPath)').Replace('.less','.min.css'))"" />
  <Exec Command=""..\packages\dotless.1.3.1.0\tool\dotless.compiler.exe" -a "%(__LessFiles.FullPath)" "$([System.String]::Copy('%(__LessFiles.FullPath)').Replace('.less','.css'))"" />
</Target>

Этот небольшой скрипт запускает консольную утилиту dotless при сборке проекта и генерирует файлы .css и .min.css. Откроем проект, запустим сборку, видим появившиеся файлы CSS:

Интеграция стилей LESS в сайт на основе ASP.NET

Очевидно, что в такой иерархии хранить генерируемые CSS очень неудобно – даже при небольшом их количестве в папке будет хаос. Поэтому еще немного поработаем руками.

Открываем файл проекта, находим сгенерированные файлы CSS и декларируем, что они должны отображаться в качестве дочернего узла в дереве проекта:

Интеграция стилей LESS в сайт на основе ASP.NET

Загружаем проект, теперь видим, что всё по полочкам:

Интеграция стилей LESS в сайт на основе ASP.NET

Совершенству нет предела – давайте немного улучшим этот способ так, чтобы CSS генерировался не для всех LESS файлов, а выборочно. Сделаем так, чтобы процессинг происходил только для тех LESS-файлов, для которых уже существует CSS-файл. Тогда, чтобы добавить процессинг очередного LESS-файла, просто создадим для него пустой CSS-файл и при сборке будет осуществляться процессинг.

Для этого идем в файл проекта и изменяем скрипт следующим образом:

<Target Name="CompileDotlessCss" AfterTargets="AfterBuild">
  <ItemGroup>
    <__LessFiles Include="**\*.less" />
    <__LessFilesToProcessMin Include="%(__LessFiles.Identity)" Condition="Exists('%(__LessFiles.RootDir)%(__LessFiles.Directory)%(__LessFiles.Filename).min.css')" />
    <__LessFilesToProcessDev Include="%(__LessFiles.Identity)" Condition="Exists('%(__LessFiles.RootDir)%(__LessFiles.Directory)%(__LessFiles.Filename).css')" />
  </ItemGroup>
  <Exec Command=""..\packages\dotless.1.3.1.0\tool\dotless.compiler.exe" -m -a "%(__LessFilesToProcessMin.FullPath)" "$([System.String]::Copy('%(__LessFilesToProcessMin.FullPath)').Replace('.less','.min.css'))"" />
  <Exec Command=""..\packages\dotless.1.3.1.0\tool\dotless.compiler.exe" -a "%(__LessFilesToProcessDev.FullPath)" "$([System.String]::Copy('%(__LessFilesToProcessDev.FullPath)').Replace('.less','.css'))"" />
</Target>

Теперь генерация CSS выполняется только для тех LESS-файлов, для которых CSS-файлы существуют. По мне – очень неплохой компромис.

Плюсы данного подхода:

Минусы данного подхода:

Выводы — для меня это на данный момент рабочий вариант того, как процессить LESS в ASP.NET проектах. Приходится немного править проект руками, однако, это компенсируется дальнейшими удобствами.

Пример проекта с таким видом процессинга на github

ASP.NET Bundling and Minification

Не так давно в ASP.NET появилось пространство имен System.Web.Optimization, которое включает в себя инструменты для клиентской оптимизации. Одним из таких механизмов является возможность создания пакетов клиентских файлов (bundle).

Идея заключается в том, что в момент исполнения приложения ASP.NET создает пакет, который объединяет несколько JS или CSS файлов, при этом минифицируя их и/или выполняя еще какой-либо серверный процессинг. Собственно, идея данного способа заключается в том, что если у вас уже используются Bundle-ы для оптимизации, то вы можете встроить в эту цепочку и процессинг LESS, используя все тот же dotless, например. Как это сделать:

Шаг 1: Подключаем dotless к проекту (описывалось ранее).

Шаг 2: Создаем класс для LESS-трансформаций:

using System.Web.Optimization;

public class LessTransform : IBundleTransform
{
    public void Process(BundleContext context, BundleResponse response)
    {
        response.Content = dotless.Core.Less.Parse(response.Content);
        response.ContentType = "text/css";
    }
}

Шаг 3: Добавляем этот класс в общий список трансформаций при определении пакета:

var stylesBundle = new Bundle("~/bundles/styles").IncludeDirectory("~/Content/Styles/", "*.less");
stylesBundle.Transforms.Add(new LessTransform());
// ...
stylesBundle.Add(lessBundle);

Теперь этот пакет можно использовать как и все остальные пакеты. Про Bundle-ы и оптимизацию можно почтитать еще тут.

Плюсы данного подхода:

Минусы данного подхода:

Выводы — для тех у кого итак используется Bundles для организации клиентского кода этот способ вполне может подойти. Из-за описаных выше минусов мне этот способ не очень нравится, поэтому я отдаю предпочтение процессингу LESS-файлов на этапе сборке при помощи dotless.

Ссылки по теме

  1. Официальный сайт LESS
  2. Библиотека dotless
Корректная обработка проблем аутентификации AJAX-запросов для приложений ASP.NET MVC ← → Создание собственного типа сборки (Build Action) в Visual Studio 2013

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

Для отображения аватара испольузется Gravatar
Можно форматрировать текст при помощи Markdown