Перейти к содержанию

Специальные параметры и сопоставление шаблонов

Примеси являются очень мощным инструментом в рамках Less. Их возможности практически ограничены лишь фантазией разработчика, а спектр применения довольно широк.

Специальные параметры

Кроме обычных локальных переменных для примеси, можно использовать специальные параметры, которые имеют несколько другое предназначение.

Переменная @arguments

Переменная @arguments представляет собой синоним для всех переданных в примесь значений при её вызове. Такое поведение может быть полезно при использовании примеси с большим количеством переменных, предназначающихся для одного свойства.

.box-shadow(@x: 0, @y: 0, @blur: 1px, @color: #333) {
  -webkit-box-shadow: @arguments;
  -moz-box-shadow: @arguments;
  box-shadow: @arguments;
}

.big-block {
  .box-shadow(2px, 5px);
}

Так как переменная @arguments содержит в себе значения всех переданных переменных, то результат будет следующим:

.big-block {
  -webkit-box-shadow: 2px 5px 1px #333;
  -moz-box-shadow: 2px 5px 1px #333;
  box-shadow: 2px 5px 1px #333;
}

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

Переменная @rest

Переменная, позволяющая указывать переменное количество передаваемых параметров. Звучит сложно, но на деле все проще:

.mixin(@width: 152px, @height: 20px, @rest...) {
  width: @width;
  height: @height;
  border: @rest;
}

.class {
  .mixin(304px, 40px, 2px, solid, #ddd);
}

Обратите внимание на троеточие (...) после переменной @rest. Если бы его не было, то переменная @rest являлась бы обычной переменной со всеми вытекающими отсюда проблемами в виде ошибки компилятора.

Первые два значения будут присвоены переменным @width и @height соответственно, а остальные значения будут доступны переменной @rest.

.class {
  width: 304px;
  height: 40px;
  border: 2px solid #dddddd;
}

Переменные @rest и @arguments можно использовать вместе:

.mixin(@width: 152px, @height: 20px, @rest...) {
  content: @arguments;
}

.class {
  .mixin(304px, 40px, 2px, solid, #ddd);
}

В этом случае свойство content получит все переданные переменные:

.class {
  content: 304px 40px 2px solid #dddddd;
}

Как и в случае с @arguments, используется крайне редко, так как такое поведение примесей избыточно и практически не имеет места применения.

Сопоставление шаблонов

Иногда требуется изменять поведение примеси в зависимости от передаваемых в неё параметров. Поведение примеси может зависеть от количества переданных переменных, их значений и некоторых других факторов, о которых будет говориться подробнее в следующей главе.

Я не стал оформлять это как пример, так как здесь идёт объяснение основ сопоставления шаблонов, но вы можете найти описанный здесь код как пример 3.4.5 в архиве, прилагаемом к этой книге.

Допустим, что имеется примесь, которая генерирует цвет фона, шрифта и рамки:

.mixin(@color, @bg, @border-color) {
  color: @color;
  background-color: @bg;
  border: 1px solid @border-color;
}

Требуется изменять цвет и фон блока, например, в зависимости от количества переданных переменных. Если переменная одна, то менять только цвет шрифта, если их две, то менять цвет шрифта и фон, иначе все параметры. Это можно сделать следующим образом:

.mixin(@color) {
  color: @color;
}

.mixin(@color, @bg) {
  color: @color;
  background-color: @bg;
}

.mixin(@color, @bg, @border-color) {
  color: @color;
  background-color: @bg;
  border: 1px solid @border-color;
}

Рассмотрим этот случай подробнее, так как такие конструкции будут часто встречаться в крупных фреймворках и хитрых проектах.

Здесь не играет роли в каком порядке будут объявлены примеси, так как компилятор сам узнает количество переданных значений при вызове примеси и сопоставит их с той примесью, к которой оно подходит. Однако, такая схема работает до тех пор, пока не появится ещё одна примесь с одинаковым количеством переменных, но об этом немного позднее.

Итак, попробуем передать одну переменную:

.test-1 {
  .mixin(#333);
}

Сперва может показаться, что такой вызов породит ошибку из-за того, что действует правило «сверху вниз» и использоваться будет последняя примесь. Но это не так.

.test-1 {
  color: #333333;
}

Хорошо, здесь сработала первая примесь, которой для работы требуется один параметр.

Попробуем передать все три параметра:

.test-3 {
  .mixin(#333, #fff, #ddd);
}

Получим код, который порождает третья примесь:

.test-3 {
  color: #333333;
  background-color: #ffffff;
  border: 1px solid #dddddd;
}

Все это хорошо работает и вроде как даже понятно, но в игру вступает любопытство:

  • Что будет, если у всех трёх примесей указаны параметры по умолчанию и примесь вызывается без значений?
  • Что будет, если у одной из примесей указаны параметры по умолчанию и примесь вызывается без значений?
  • Что будет, если у одной из примесей указаны параметры по умолчанию, у другой частично и примесь вызывается с одним значением?
  • Что будет, если объявлены примеси с одинаковым числом параметров?

Ответы на эти вопросы я предлагаю узнать и понять с помощью примеров описанных ниже:

Пример 3.5.1

Что будет, если у всех трёх примесей указаны параметры по умолчанию и примесь вызывается без значений?

Я сокращу количество примесей до двух, чтобы не показывать один и тот же код от примера к примеру.

.mixin(@color: #777) {
  color: @color;
}

.mixin(@color: #000, @bg: #fff) {
  color: @color;
  background-color: @bg;
}

Итак, у всех примесей указаны значения параметров по умолчанию. Можно предположить, что при вызове будут использованы обе примеси, так как обе они подходят под шаблон вызова.

.class {
  color: #777777;
  color: #000000;
  background-color: #ffffff;
}

Пример 3.5.2

Что будет, если у одной из примесей указаны параметры по умолчанию и примесь вызывается без значений?

.mixin(@color: #777) {
  color: @color;
}

.mixin(@color, @bg) {
  color: @color;
  background-color: @bg;
}

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

.class {
  color: #777777;
}

Пример 3.5.3

Что будет, если у одной из примесей указаны параметры по умолчанию, у другой частично и примесь вызывается с одним значением?

В этом случае все будет зависеть от того, какой параметр имеет значение по умолчанию у второй примеси.

Допустим, что первый:

.mixin(@color: #777) {
  color: @color;
}

.mixin(@color: #333, @bg) {
  color: @color;
  background-color: @bg;
}

.class {
  .mixin(#000);
}

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

SyntaxError: wrong number of arguments for .mixin (1 for 2) in _styles.less on line 11, column 3:
10 .class {
11   .mixin(#000);
12 }

Если же значение по умолчанию указано у второго параметра, то компилятор спокойно выдаст:

.class {
  color: #000000;
  background-color: #ffffff;
}

Пример 3.5.4

Что будет, если объявлены примеси с одинаковым числом параметров?

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

Вернёмся к полному коду примера 3.4.5 и добавим зловредную примесь. Для наглядности поменяем параметры местами:

.mixin(@color) {
  color: @color;
}

.mixin(@color, @bg) {
  color: @color;
  background-color: @bg;
}

.mixin(@color, @bg, @border-color) {
  color: @color;
  background-color: @bg;
  border: 1px solid @border-color;
}

.mixin(@bg, @color) {
  color: @color;
  background-color: @bg;
}

Компилятор будет недоволен происходящим, но работу свою он знает и выдаст следующее:

.class {
  color: #333333;
  background-color: #f5f5f5;
  color: #f5f5f5;
  background-color: #333333;
}

Получается, что в случае, когда объявлены примеси с одинаковыми именами и количеством параметров на выходе будут иметься свойства сразу двух примесей.

Мысли и советы

Старайтесь не использовать переменные @arguments и @rest там, где это не нужно. Такой подход может запутать новых разработчиков вашей команды или даже вас самих.

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