Less.js: Добавлена ​​поддержка переменных «по умолчанию» (аналогично !default в SASS).

Созданный на 3 дек. 2013  ·  35Комментарии  ·  Источник: less/less.js

Мы рассматриваем возможность перехода с SASS на LESS, но основным препятствием для нас является отсутствие функции «переменной по умолчанию» (см. http://sass-lang.com/documentation/file.SASS_REFERENCE.html#variable_defaults_)

Эта функция невероятно полезна при распространении вашего кода SASS в виде библиотеки, где переменные служат своего рода «API» для ваших пользователей. В таком случае все, что нужно сделать пользователю, это убедиться, что его переменные включены заранее, чтобы переопределить значения по умолчанию, которые он хочет изменить, и вуаля! не нужно возиться с порядком (возможно) многих файлов, которые могут содержать переменные, которые необходимо переопределить.

Я начал работать над реализацией, еще не написал никаких тестов, но, надеюсь, мы сможем использовать этот запрос на вытягивание в качестве отправной точки для обсуждения: https://github.com/less/less.js/pull/1705 .

Я выбрал следующий синтаксис:

?foo: value;

в отличие от способа SASS:

<strong i="13">@foo</strong>: value !default;

по двум причинам - это более лаконично, а синтаксис !default может в будущем создать потенциальные проблемы с разбором выражений справа (но я могу ошибаться в этом).

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

Ваше здоровье,
Фил

consider closing feature request low priority

Самый полезный комментарий

Я собирался предложить статью, которую написал, а потом увидел, что @seven-phases-max ссылается на нее. 😄 Поверьте нам, то, что вы просите, уже существует! Но вы должны понимать оценку переменных Лесса, чтобы понять, как/почему она существует.

Все 35 Комментарий

См . Документацию .


Обновление: удален мой предыдущий комментарий к этому сообщению, чтобы не смущать будущих посетителей (здесь я пытался ответить на вопрос «как определить переменную, если она еще не определена» и упустил момент, это «проблема XY» и правильный ответ для Sass-like !default : в Less вам не нужно ничего подобного, потому что необходимая функциональность («(пере)определение после использования») уже обеспечивается тем, как работают переменные Less).

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

библиотека.less

<strong i="7">@width</strong>: 0;
.mixin {
  width: @width;
}

пользователь.less:

<strong i="11">@import</strong> "library.less"; //first declaration of <strong i="12">@width</strong>
<strong i="13">@width</strong>: 1; //this will override <strong i="14">@width</strong> defined previously
.class {
  .mixin();
}

компилируется в:

.mixin {
  width: 1;
}
.class {
  width: 1;
}

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

Мы делаем большую структуру компонентов, реализованную в виде иерархии классов. Мы объявляем переменные для каждого компонента в отдельном файле... в нашей базовой теме. Затем мы «извлекаем» отсюда темы, которые хотят изменить эти переменные. Пользователи также могут сделать это, чтобы настроить тему в соответствии со своими потребностями.

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

На сегодняшний день лучшим решением этих проблем является _всегда_ определение переменных как "!default" (в терминах Sass). Это позволяет пользователям просто сначала войти в систему и установить свои значения, прежде чем эти значения будут использованы для расчета дальнейших значений. Таким образом, для наших пользователей управление временем довольно простое. Поскольку это всегда имеет место для каждой переменной во всех наших темах, синтаксические трудности, подобные тем, которые были предложены выше, были бы довольно обременительными, а также подверженными ошибкам.

Мы хотели бы добавить другие функции в Less, когда мы продолжим. Я думаю, что наш (необычно?) крупномасштабный вариант использования может быть полезной проверкой того, что необходимо для расширения набора языков/функций.

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

Еще раз спасибо за ваше время и внимание!

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

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

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

$panel-base-color: $neutral-light-color !default;
$panel-header-color: $base-color !default;
$panel-frame-border-width: 1px !default;
$panel-header-font-size: round($font-size * 1.15) !default;
$panel-body-border-color: $neutral-dark-color !default;

$panel-light-header-color: #000 !default;
$panel-light-header-background-color: #fff !default;
$panel-light-tool-background-image: 'tools/tool-sprites-dark' !default;
$panel-light-body-border-color: $neutral-dark-color !default;
$panel-ignore-frame-padding: true !default;

Этот файл устанавливает различные значения Panel для темы, которая имеет цепочку базовых тем глубиной 4. Эти базовые темы имеют свои собственные значения переменных для Panel, но они загружаются первыми, поэтому переопределяют их. Если пользовательская тема не является производной от этой темы и не предоставляет собственные значения.

Этот шаблон повторяется _a lot_ :)

Хорошо, почему бы просто не переопределить @panel-base-color в вашем основном меньшем листе? Переменные LESS являются глобальными, поэтому победителем становится то, что произойдет последним.

<strong i="7">@import</strong> 'theme.less';

@panel-base-color: red;

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

У нас нет «основного меньшего листа» :) Я ценю вашу помощь и предложения, но у нас было много внутренних дискуссий по этому поводу, и они, как правило, продолжаются некоторое время. Достаточно сказать, что мы считаем, что нам нужна установка переменной по умолчанию, как мы предложили здесь. Эта функция существует в Sass, и мы нашли ее очень полезной для упрощения и предоставления пользователям гибкости при настройке наших тем.

Мне любопытно, означает ли все это, что этот запрос на включение или что-то подобное будет когда-либо принято? Мы будем рады изменить синтаксис, если это вызывает возражения.

У нас нет «основного меньшего листа» :)

Просто замените «ваш основной меньший лист» в ответах выше на «любой из меньших листов пользователя». Пока что кажется, что SASS !default (какой бы синтаксис у него ни был) придуман для решения проблемы, которой нет в LESS.
@dongryphon , могу конечно ошибаться, но вы еще не обосновали почему нельзя использовать решение, предложенное @SomMeri и @Soviut.

Иными словами, представьте, что вы не импортируете, а вместо этого все ваши переменные находятся на одном листе. Это фактически то, что делает импорт. Таким образом, в этой ситуации, если у вас есть два объявления переменных с одинаковыми именами, победит последнее объявление на листе.

@base-color: green;

div {
    background: @base-color;
}

@base-color: red;

Поскольку базовый цвет снова объявляется в конце, базовый цвет, используемый в div, будет красным. Он будет скомпилирован в:

div {
    background: red;
}

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

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

Для пользователей библиотеки не так уж сложно сказать, что они переопределяют переменные после импорта (или импортируют файл переменных после импорта) по сравнению с добавлением большего синтаксиса к меньшему. Мы думаем, что это одно из преимуществ меньшего, что вы можете делать то же самое, что и sass, но большую часть времени с более простым синтаксисом.

Это противоречит тому, что может подумать программист JavaScript, но идея в том, что он ближе к css.

Пожалуйста, не могли бы вы пояснить, почему просить потребителей подумать о порядке невозможно или нежелательно? Мы примем функции с сильными вариантами использования, но мы должны быть строгими, чтобы избежать раздувания языка и сложности.

См. #1109 #1104 #313

И просто чтобы уточнить, мы ни в коем случае не говорим «нет». Мы пытаемся показать ему, что эта функция может уже существовать.

Я добавил некоторую информацию в less-docs

Функция закрытия как уже доступной (в меньшем виде) (http://lesscss.org/features/#variables-feature-default-variables).

Я думаю, что эта путаница часто возникает, потому что, хотя _синтаксис_ перекрывается, SASS "выполняется" сверху вниз, а LESS - нет, а вместо этого работает больше как CSS. LESS похож на CSS+ (CSS с дополнительными функциями), а SASS похож на «PHP с синтаксисом CSS». Интересно, есть ли способ (если необходимо) провести это различие.

Пожалуйста, не могли бы вы пояснить, почему просить потребителей подумать о порядке невозможно или нежелательно? Мы примем функции с сильными вариантами использования, но мы должны быть строгими, чтобы избежать раздувания языка и сложности.

Потому что мы учим пользователей библиотек (и потребителей) никогда не редактировать библиотечные коды самостоятельно. Отсутствующая функция !default означает, что мы должны сделать одну из этих двух вещей — обе одинаково плохие:

  • Пользователи должны сами редактировать таблицу стилей библиотеки, чтобы редактировать переменные, в которых они объявлены (что является своего рода запретной вещью, усложняющей обновление библиотеки) или,
  • Библиотека должна предоставить две таблицы стилей: одну для переменных по умолчанию и другую для фактических правил. Затем пользователь должен @импортировать первый, затем объявить свои собственные переменные, а затем @импортировать второй. Это сложнее, чем должно быть, особенно хранить эти два файла в одной и той же версии.

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

my-color: red;
<strong i="15">@import</strong> "./my-library.less";

Вместо:

<strong i="19">@import</strong> "./my-library-variables.less";
my-color: red;
<strong i="20">@import</strong> "./my-library-rules.less";

Я думаю, что вам следует пересмотреть этот вопрос.

@arcanis На самом деле я не понимаю, почему вы так думаете:

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

В Less абсолютно такой же дизайн библиотеки:

<strong i="11">@import</strong> "./my-library.less";
@my-color: red;

Я предполагаю, что вам просто не хватает "ленивой загрузки" (и точно такой же пример используется в документах , чтобы сказать no, thanks! до !default ).

Я думаю, вам просто не хватает "ленивой загрузки"

@seven-phases-max WTF, ты прав. Черт возьми, мне, вероятно, следует удалить весь свой ответ (и только что сделал). Вы говорите мне, что можете импортировать загрузчик и просто переопределить определенные переменные, и это просто сработает?

Я не поверил вам и провел собственные тесты, чтобы убедиться в этом. Как я это пропустил?? Я думаю, это потому, что в каждой статье, которую я видел на Bootstrap, которая включала настройку, рекомендовалось скопировать файл variable.less и установить свои собственные значения, что, конечно, вызывает проблемы, когда в библиотеку добавляется больше переменных. И я думаю, у меня сложилось впечатление, что селекторы выводятся в непосредственной точке импорта. Всегда ли переменные «лениво загружались» таким образом?

Это, должно быть, самая важная функция, которую часто упускают из виду в Less. Я никогда не видел записи в блоге о Less или библиотеке Less, в которой упоминалась бы эта функция. Даже со всем, что было в этой ветке, и с документацией, было не сразу понятно, что это значит для реальных библиотек. Я думал, что все просто говорят, что вы можете переопределить переменные, объявленные ранее, установив их позже, и я так и не понял, что это повлияет на оценку переменных из ранее импортированных документов.

Я не могу поверить, что я никогда не получил это до сих пор. Это меняет в основном все, что касается того, как я структурирую свои файлы less, и в документах есть крошечное упоминание, которое не демонстрирует вывод.

Возможно, причина, по которой мы получаем так много запросов на функцию !default в Less, заключается в том, что мало кто интуитивно понимает эту функцию или понимает ее из краткого примера из документации (включая меня, разумеется).

По крайней мере, спасибо, что объяснили еще раз, @seven-phases-max!

Упс. Что ж, ты прав. Я тоже неправильно понял документы, мой плохой!

@Soviut , ты чертовски гений! Я понятия не имел, что объявления переменных работают так в SASS/LESS. Спасибо!

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

Кроме того, обратите внимание: @spikesagal в отношении вашего утверждения: «Я понятия не имел, что объявления переменных работают так в SASS/LESS». Насколько мне известно, поведение переменных неодинаково между двумя языками, поскольку SASS и LESS оценивают вещи очень по-разному.

И они убивают эту функцию в BS4 из-за перехода на Sass... :-1: Все еще очень грустно из-за этого перехода.

Итак, начинается борьба за дополнительные переменные !default: [twbs/bootstrap#17418]

Ну, они не могут убить функцию Less, каким-либо образом изменив источники SCSS. (Технически это даже не «функция» (например, какое-то «синтезированное поведение»), а фундаментальное свойство/следствие ленивой оценки). Пока существует Less-версия BS (это просто вопрос количества людей, желающих внести свой вклад) и есть хотя бы одна глобальная переменная, вы всегда сможете переопределить эту переменную.

Хорошо. Официальная альфа-версия бутстрапа v4 перенесена на Sass. Это, по крайней мере, в моем понимании, убивает меньше в их официальной документации — и это также означает убивает поддержку этой функции, потому что в Sass нет такой вещи, как ленивая загрузка переменных. Они поддерживают только !default, что в некотором роде означает: «О, да, мы разрешаем вам переопределять наши переменные только один раз извне, и только если мы разрешаем это делать. Так что, если мы забудем предоставить вам доступ к переменную, просто опуская !default, вы в значительной степени облажались и должны переопределить весь проклятый селектор в своих файлах. Смиритесь с этим».

и это означает также отказ от поддержки этой функции, потому что в Sass нет такой вещи, как отложенная загрузка переменных.

Ну, это означает только «они убивают его в Bootstrap» (что, очевидно, выходит за рамки этого потока — пока нет Less версии BS, мы не должны заботиться о каких-либо функциях Bootstrap в _this_ репозитории, не так ли?).

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

Мы придерживаемся меньшего :smile:

У меня есть вариант использования первоначально заявленной дилеммы. Рассмотрим следующий упрощенный пример:

  1. У меня есть файл меньшего размера, который определяет размер шрифта h1, что-то вроде этого
    <strong i="8">@font_size__h1</strong> = 30px;
  2. У меня есть (из-за отсутствия лучшей фразы) плагин, который содержит отдельный файл LESS, в котором используется объявление @font_size__h1 . Так что я <strong i="12">@import</strong> (reference) "path/to/file.less"; это. Нет проблем, теперь он доступен.
  3. Теперь я использую этот «плагин» на новом сайте, на котором нигде не определено @font_size__h1 . В этот момент было бы здорово сказать: «Если @font_size__h1 определено, используйте это значение. Если оно не определено, используйте значение, которое я определяю здесь».

Насколько я вижу, пункт 3 в настоящее время невозможен.

@theMikeD

Насколько я вижу, пункт 3 в настоящее время невозможен.

Похоже, вы не читали ветку:

<strong i="10">@import</strong> "path/to/file.less";
<strong i="11">@import</strong> "here.less"; // if it's not defined <elsewhere>, then use the value I define <here>
<strong i="12">@import</strong> "elsewhere.less";

что, конечно, можно свести к:

<strong i="16">@import</strong> "path/to/file.less"; // <- define default value there
@font-size-h1: foo;          // if it's not defined here then use the value defined above

который в основном тот же пример, что и http://lesscss.org/features/#variables -feature-default-variables

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

что, конечно, можно свести к:
@import "путь/к/file.less"; // <- определяем там значение по умолчанию
@font-size-h1: foo; // если оно не определено здесь, используйте значение, определенное выше

Это противоположно тому, что мне нужно. Это перезапишет значение @font-size-h1 в path/to/file.less локальным значением, несмотря ни на что. Мне нужно, чтобы значение в локальном файле использовалось только в том случае, если:
а) path/to/file.less не загружен, или
б) значение '@font_size__h1 is not set in path/to/file.less`

IOW локальное значение @font-size-h1: foo; всегда будет присутствовать в локальном файле, но должно быть переопределено, если оно установлено в path/to/file.less

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

Спасибо, в любом случае.

Похоже, вы не понимаете, о чем я спрашиваю

Я бы посоветовал вам начать с некоторых основ Less variable, чтобы освежить ваше представление:

(потому что кажется, что вы просто пытаетесь думать обо всем этом в императивной манере, подобной C/PHP, в то время как в Less/CSS это полностью «декларативно вверх ногами»).
Об этом говорили до смерти все эти годы, !default не может добавить _ничего_ нового по сравнению с родным переопределением переменной Less. Период. (Мы уже сталкивались с этим много раз: если кто-то думает, что нашел какой-то вариант использования !default в Less, это означает не что иное, как его непонимание семантики переменных Less).

Мне нужно, чтобы значение в локальном файле использовалось только в том случае, если:
а) path/to/file.less не загружен, или
б) значение @ font_size__h1 не установлено в path/to/file.less

Тогда просто наоборот:

@font-size-h1: foo;
<strong i="27">@import</strong> "path/to/file.less"; 

тада!

...где я и приземлился, как я уже сказал.

Я собирался предложить статью, которую написал, а потом увидел, что @seven-phases-max ссылается на нее. 😄 Поверьте нам, то, что вы просите, уже существует! Но вы должны понимать оценку переменных Лесса, чтобы понять, как/почему она существует.

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

без приложения
/сетка/сетка.лесс

Поскольку сетка является компонентом, забудьте о добавлении чего-либо здесь или о добавлении кода после файла grid.less.
Я не понимаю, как меньше покрывает эту проблему. Scss предлагает эту функцию по уважительной причине.

@geri777

Но если определенная переменная извне уже определена, она должна иметь приоритет.

Меньше вычисляет, как CSS.

.css {  
  --color: blue;
  color: var(--color);  // --color will be red
  --color: red;
  border-color: var(--color);  // --color will still be red, red is the scope's final value
}

.less {  
  <strong i="10">@color</strong>: blue;
  color: @color;  // <strong i="11">@color</strong> will be red
  <strong i="12">@color</strong>: red;
  border-color: @color;  // <strong i="13">@color</strong> will still be red, red is the scope's final value
}
````

Scss, instead, doesn't mimic CSS evaluation and instead evaluates more like, say, PHP.

```scss
.scss {  
  $color: blue;
  color: $color;  // $color will be blue
  $color: red;
  border-color: $color;  // $color will be red
}

Итак, в Sass/SCSS, чтобы переопределить значение корневой переменной, вы вынуждены делать две вещи:

  1. Вы должны пометить все свои объявления переменных с помощью !default
  2. Вы должны вставить глобальные переменные перед этими объявлениями по умолчанию.

Как в:

// main.scss
<strong i="22">@import</strong> "overrides.scss";
<strong i="23">@import</strong> "library.scss";

// overrides.scss
$color: red;

// library.scss
$color: blue !default;

.scss {
  color: $color;
}

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

// main.less
<strong i="27">@import</strong> "library.less";
<strong i="28">@import</strong> "overrides.less";

// overrides.less
<strong i="29">@color</strong>: red;

// library.less
<strong i="30">@color</strong>: blue;

.less {
  color: @color;
}

Поэтому вам не нужно !default , потому что Less всегда примет ваше окончательное значение.

Думайте об оценке Less как о каскаде CSS. Это просто работает. Окончательная декларация побеждает.

Была ли эта страница полезной?
0 / 5 - 0 рейтинги