Интерполяция переменных
Если вы раньше работали с PHP или Ruby, то должны были хотя бы раз использовать интерполяцию переменных. Интерполяция переменных — это получение значения одной переменной в зависимости от другой или других переменных.
В Less есть конструкции типа @{}
, которые технически похожи на интерполяцию переменных. Такие конструкции могут быть использованы в селекторах, переменных и строках. Далее будут рассмотрены все популярные и почти не применяемые варианты использования интерполяции переменных.
Я не зря вынес эту интересную тему в отдельный раздел, так как с помощью интерполяции переменных можно творить чудеса и делать ваш проект или библиотеку максимально настраиваемыми.
Интерполяция переменных в строках
Начинать нужно с основ, так как, если есть желание овладеть технологией в полной мере, то именно основы дадут понимание того, как что-то работает или почему это что-то не работает.
Основами интерполяции переменных являются строки. Именно в них чаще всего может понадобиться подставить значение одной переменной и получить необходимое значение другой переменной, к которой относится эта строка. Это позволяет настраивать пути до изображений, шрифтов, или библиотек не используя поиск по всему проекту для их редактирования.
Интерполяция переменных в строках, как это ни странно, происходит в строках тогда, когда название переменной оборачивается в специальную конструкцию, которая выглядит следующим образом:
@russia-city-odintsovo: Одинцово;
@english-city-odintsovo: Odintsovo;
@russia-hello: "Привет, @{russia-city-odintsovo}!";
@english-hello: "Hello, @{english-city-odintsovo}!";
Конструкции @{russia-city-odintsovo}
и @{english-city-odintsovo}
интерполируются и если вызвать эти переменные, то вместо них будет подставлено название города на соответствующем языке:
.hello-russia {
content: "Привет, Одинцово!";
}
.hello-english {
content: "Hello, Odintsovo!";
}
Пример 3.2.1
В этом примере мы подробно разберём то, как происходит интерполяция переменных. Инициализируем несколько переменных и поместим их в начале файла _styles.less
так, как это было описано в начале главы:
// Variables
@icon-font-name: fontawesome-webfont;
@icon-font-path: "../fonts";
Чтобы воспользоваться шрифтом FontAwesome, в проекте необходимо использовать директиву @font-face
. Ради экономии места я не стал вставлять сюда весь код примера, он, как и любой код в примерах, будет доступен в архиве.
@font-face {
font-family: 'FontAwesome';
src: url('@{icon-font-path}/@{icon-font-name}.eot?v=4.3.0');
src: url('@{icon-font-path}/@{icon-font-name}.eot?#iefix&v=4.3.0') format('embedded-opentype'),
...
}
На выходе компилятора получим следующий код:
@font-face {
font-family: 'FontAwesome';
src: url('../fonts/fontawesome-webfont.eot?v=4.3.0');
src: url('../fonts/fontawesome-webfont.eot?#iefix&v=4.3.0') format('embedded-opentype'),
...
}
Обратите внимание, если поменять вызов переменной @{icon-font-path}
на @icon-font-path
то, вместо того, чтобы вставить значение переменной, компилятор попросту принял @icon-font-path
за часть строки. Именно в этом и заключается суть интерполяции переменной — грубо говоря, она заставляет интерпретатор отличать строки от переменных и при необходимости подставлять их значение в строку.
@font-face {
font-family: 'FontAwesome';
src: url('@icon-font-path/fontawesome-webfont.eot?v=4.3.0');
src: url('@icon-font-path/fontawesome-webfont.eot?#iefix&v=4.3.0') format('embedded-opentype'),
...
}
Интерполяция переменных в селекторах и свойствах
Иногда требуется делать код гибким вплоть до того, чтобы селекторы изменяли своё имя в зависимости от значения переменных. Наиболее вероятно, что такое поведение пригодится в библиотеках или фреймворках, в которых разработчики хотят разрешить изменять префикс классов их детища.
Интерполяция переменных в селекторах по конструкции полностью повторяет таковую в строках:
@lang-prefix: site;
@lang-russia: rus;
@lang-english: eng;
.@{lang-prefix}-@{lang-russia} {
content: "@{lang-russia}";
}
.@{lang-prefix}-@{lang-english} {
content: "@{lang-english}";
}
Компилятор сам позаботится о формировании имени класса из двух переменных, инициализированных ранее:
.site-rus {
content: "rus";
}
.site-eng {
content: "eng";
}
Никто не запрещает использовать интерполяцию и в рамках свойств. Конструкция вида:
@property: color;
.widget {
@{property}: #0ee;
background-@{property}: #999;
}
будет компилироваться в:
.widget {
color: #0ee;
background-color: #999;
}
Интерполяция переменных внутри переменных
Важно понимать, что хороший код должен быть адаптируемым. То есть имена селекторов и значения свойств должны меняться в зависимости от контекста использования, если это требуется разработчику.
С помощью примесей, которые будут рассматриваться позднее, можно писать адаптируемый и переиспользуемый код. Для этого в примесях используются значения, передаваемые как аргументы. В зависимости от передаваемых значений, можно формировать названия переменных, которые в последствии будут вызываться.
Пример 3.2.2
Так как примеси мы ещё не проходили, а интерполяцию переменных внутри переменных показать и объяснить нужно, то представим себе ситуацию, в которой у нас есть несколько переменных:
// Variables
@color-prefix: color;
@color-grey: #fafafa;
@color-red: #ffebee;
Разработчику нужно выбрать серый и красный цвет, но префикс переменных, в которых хранится искомое значение, ему не известен — он хранится в переменной @color-prefix
.
Чтобы получить цвета, разработчику нужно построить переменную на основе уже известной ему переменной:
.grey {
background-color: e("@{@{color-prefix}-grey}");
}
.red {
background-color: e("@{@{color-prefix}-red}");
}
Обратите внимание на то, что здесь используется экранирование, так как конструкция внутри очень похожа на конструкцию переменной, но в то же время не являющаяся ей. Далее нужно обернуть конструкцию в @{}
столько раз, сколько раз планируется собирать переменную из частей. Проще говоря, мы конструируем строку, а потом получаем значение переменной, имя которой совпадает с этой строкой.
Однако, ложкой дёгтя является то, что при такой интерполяции значение всегда приводится к строке. Это поведение стоит отнести к частному случаю, который также имеет своё решение, пусть и не самое элегантное.
Частый случай интерполяции переменных внутри переменных
В некоторых случаях появляется необходимость получить значение переменной, имя которой задано в другой переменной. Примером может служить примесь, которая выставляет отступы блока в зависимости от переданной в неё строки. Рассмотрим эту проблему более детально.
Допустим, разработчик опытным путем или на глаз определил, что все блоки на странице делятся на два типа: с маленьким и большим отступом. Здесь под отступом стоит понимать, как внутренний, так и внешний отступ. Для стандартизации кода эти размеры будут вынесены в переменные:
// Small
@margin-small: 40px;
@padding-small: 20px;
// Large
@margin-large: 80px;
@padding-large: 40px;
В будущем разработчик напишет примесь, но, так как сейчас я о примесях ещё ничего не говорил, вместо неё будем использовать переменную @size
. В этой переменной будет содержаться имя размера блока, то есть small
или large
. Попробуем получить отступы для произвольного блока, используя описанный выше метод интерполяции переменных внутри переменных:
// Small
@margin-small: 40px;
@padding-small: 20px;
@size: "small";
.block {
margin: ~"@{margin-@{size}}";
}
Этот код будет скомпилирован в CSS без ошибок и свойству margin
будет присвоено значение 40px
. Однако, в этой синтаксической конструкции скрывается немного чёрной магии. Понять проблему поможет простая операция сложения, которую необходимо провести с интерполяцией. Давайте попробуем сложить внешний и внутренний отступ.
// Small
@margin-small: 40px;
@padding-small: 20px;
@size: "small";
.block {
margin: ~"@{margin-@{size}}" + ~"@{padding-@{size}}";
}
Вот это поворот! Ошибка!
ParseError: Unrecognised input in _styles.less on line 8, column 32:
7 .block {
8 margin: ~"@{margin-@{size}}" + ~"@{padding-@{size}}";
9 }
Не буду томить читателя своими догадками и анализом происходящего — Less не умеет складывать строки. Функция экранирования возвращает строку. Да, вот так вот просто — сложить строку со строкой или строку с числом в Less нельзя. По крайне мере, в версии 2.7.1
это сделать нельзя. Впрочем, эта возможность присутствует в Sass и Stylus.
Так как же решить возникшую проблему? — использовать интерполяцию переменных с помощью конструкции @@
. К сожалению, для этого придётся ввести дополнительную переменную для каждого слагаемого.
// Small
@margin-small: 40px;
@padding-small: 20px;
@size: "small";
.block {
@margin: "margin-@{size}";
@padding: "padding-@{size}";
margin: @@margin + @@padding;
}
После компиляции свойству margin
будет присвоено значение 60px
. Это происходит вследствие того, что в переменной @margin
после интерполяции находится имя переменной margin-small
. Точно такая же ситуация со значением в переменной @padding
. При вызове переменной с помощью конструкции @@
на её место подставляется значение переменной, имя которой указано в строке.