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

Работа с переменными и примесями

Чтобы компилятор выполнял хоть сколько-нибудь полезные действия, интерпретируя JavaScript-код — ему нужны значения, которые он может получить из переменных. Для того, чтобы получить значения переменных, необходимо использовать один из представленных ниже вариантов синтаксиса:

.test-js {
  @test: 123;
  content: ` @{test}`;
  content: `this.test.toJS() `;
}

Оба варианта хорошо работают с локальными переменными. С глобальными переменными работает лишь первый способ, так как this, во втором случае, явно указывает на контекст, то есть селектор. Без контекста test.toJS() работать не будет.

Результатом компиляции будет:

.test-js {
  content: 123;
  content: '123';
}

Далее я приведу несколько примеров работы с переменными, используя JavaScript-код в Less:

.test-js {
  // Интерполяция
  @world: 'world';
  content: ~` 'hello' + ' ' + @{world}`;

  // Списки
  @list: 1, 2, 3;
  @list-js: ~` @{list}.join(', ') `;
  content: @list-js;
}

Компилятор создаст два свойства content и присвоит им вполне валидные значения:

.test-js {
  content: hello world;
  content: 1, 2, 3;
}

Первый пример демонстрирует возможность интерполяции строк прямиком в JavaScript, второй — работу со списками.

Немного подробнее остановимся на списках. Ранее я уже говорил, что списки — это альтернатива массивам в JavaScript. Списки в Less можно итерировать и узнавать их длину. Тот список, что определён в переменной @list никаких вопросов не вызывает:

.test-js {
  @list: 1, 2, 3;
  content: length(@list);
  content: extract(@list, 1);
}

// На выходе получаем
.test-js {
  content: 3; // Длина
  content: 1; // Первый элемент массива
}

А вот значение переменной @list-js списком не является, так как на выходе JavaScript-кода всегда находится строка:

.test-js {
  @list: 1, 2, 3;
  @list-js: ~` @{list}.join(', ') `;
  content: length(@list-js);
  content: extract(@list-js, 1);
}

// На выходе получаем
.test-js {
  content: 1; // Длина
  content: 1, 2, 3; // Первый элемент массива
}

Примеси

Наиболее очевидным применением возможностей JavaScript-кода в Less является создание примесей, которые на вход получают какое-то количество переменных, обрабатывают их, используя JavaScript и возвращают строку, как результат.

Конечное число переменных

Самым простым способом получить значения из переменных в Less является следующая функция:

;(function(a, b) {
  return a + b
})('@{a}', '@{b}')

Используя обёртку в виде примеси, её можно представить в удобном для использования виде:

.mixin(@a, @b) {
  @js: ~`(function(a, b) {return a + b;}) ('@{a}', '@{b}') `;

  content: @js;
}

.test-js {
  .mixin(1, 2);
}

Результатом компиляции будет являться сумма двух чисел, переданных, как аргументы примеси .mixin():

.test-js {
  content: 12;
}

Неопределённое число переменных

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

;(function(args) {
  return args
})('@{arguments}')

Записывая эту функцию в переменную и используя примесь, у которой на вход подаётся переменное количество аргументов, получим следующий less-код:

.mixin(...) {
  @js: ~`(function(args){return args;}) ('@{arguments}') `;

  content: @js;
}

.test-js {
  .mixin(3, 123);
}

И, как я уже сказал, после компиляции будет доступен массив всех переданных значений:

.test-js {
  content: [3, 123];
}

В Less с таким результатом сделать ничего не получится (мешают квадратные скобки), поэтому на практике лучше всего использовать следующую модификацию предложенной функции:

;(function(args) {
  return args
})(
  (function() {
    var args = '@{arguments}'
    return args.replace(/^\[|\]$/g, '')
  })()
)

Этот вариант записи удаляет квадратные скобки, используя метод replace(), при этом делая получаемый на выходе массив немного лучше:

.mixin(...) {
  @js: ~`function(args){return args }(
      function(){var args= '@{arguments}' ;return args.replace(/ ^\[|\]$/g, '') }(

        )
    ) `;

  content: @js;
}

.test-js {
  .mixin(3, 123);
}

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

.test-js {
  content: 3, 123;
}

Преобразование значений

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

В приведённом ниже примере последнему в списке значению добавляется единица измерения deg:

;(function(args) {
  return (
    (args = args || '0, 0, 0, 0'),
    (args = args.replace(/,\s*\d+$/, function(args) {
      return args + 'deg'
    }))
  )
})(
  (function() {
    var args = '@{arguments}'
    return (args = args.replace(/^\[|\]$/g, ''))
  })()
)

В итоге примесь имеет вид:

.rotate3d(...) {
  @js: ~`(
      function(args) {return args = args || '0, 0, 0, 0',
      args = args.replace(/, \s* \d+ $/, function(args) {return args + 'deg'})}
    ) (
      (
          function() {var args = '@{arguments}' ; return args = args.replace(
              / ^\[|\]$/g,
              ''
            )}
        )
        ()
    ) `;

  transform: rotate3d(@js);
}

.test-js {
  .rotate3d(1, 0, 0, 50);
}

После компиляции получится отформатированное значение свойства transform:

.test-js {
  transform: rotate3d(1, 0, 0, 50deg);
}

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

.test-js {
  transform: rotate3d(0, 0, 0, 0deg);
}

Описанные в этой главе примеры доступны под номерами 6.1.1 - 6.1.4 и работают только с компиляторами, написанными на JavaScript.

Выводы и мысли

Да, Less умеет интерпретировать JavaScript-код, записанный в переменной или в значении свойства, но получаемая от этого польза слишком мала и не покрывает потраченных на это усилий. Используя JavaScript в Less вы загрязняете его и усложняете для восприятия.