Less.js: 允许守卫使用未定义的变量

创建于 2013-07-04  ·  46评论  ·  资料来源: less/less.js

.guard () when (@variable) {
  a {
    color: red;
  }
}

变量未定义,因此没有输出。

<strong i="8">@variable</strong>: true;

.guard () when (@variable) {
  a {
    color: red;
  }
}

变量已定义,因此输出:

a {
  color: red;
}
feature request low priority support as plugin

最有用的评论

这个请求有什么动静吗? 一段时间以来,我一直担心未定义的变量检查。 目前,我有一个远非理想的工作......

p {
  & when not (@body-text-color = null) {
    color: @body-text-color;
  }
}

但是,要使其正常工作,该变量必须存在并且默认定义为null 。 如果我可以首先检查变量是否存在,这将更加有效/灵活。

所有46条评论

+1
这也可以用于在库上设置基本颜色。
想象以下情况:
您有一个特定应用程序的较少文件,例如网站的主菜单。
您将这个less文件导入到主less文件中,其中您的基本颜色被定义为变量。
现在,如果您在主菜单的 less 文件中使用这些基色,它们将取决于主文件。
如果您可以检查变量是否存在,您可以回退到模块定义的颜色。
这是我如何想象菜单较少文件的示例:

@menu-base-color: white;
@menu-base-color: @base-color when (@base-color);
...or...
@menu-base-color: @base-color when isset(@base-color);

+1,我们已经有istextisnumberisdefinedisundefined将大大有助于减少主题。

即使用valueuserValue如果isdefined

在尝试使用 LESS 为语义 UI 设置主题约定时,我经常遇到这个问题。

对此+1 - 真的很棒。

也为我+1

添加is-defined函数没什么大不了的(我希望它会在 v2 发布后立即出现在第一个插件中)。 虽然说实话,可悲的是那些想要这个功能的人真的错过了一个事实,即上面的两个用例都是可以解决的(在许多情况下,甚至使用更紧凑的代码)没有任何类似的功能。

@InitArt的用例:

// library.less
<strong i="9">@variable</strong>: false;

a when (@variable) {
    color: red;
}

// ......................................
// user.less
<strong i="10">@import</strong> "library.less"
<strong i="11">@variable</strong>: true;

@nemesis13的用例:

// library.less
@base-color: green;
@menu-base-color: @base-color;

.menu {
    background-color: @menu-base-color;
}

// ......................................
// user.less
<strong i="16">@import</strong> "library.less"
@base-color: white;
// or:
@menu-base-color: black;
// or whatever...

即总结:而不是测试是否定义了变量,只需为该变量提供默认值,因为变量值是可自由覆盖的。

谁能回答为什么 max 的例子对他们来说不可行? 我只能
假设这是因为您不知道变量是否在上面定义或
低于进口? 或者是别的什么?
我们不会考虑没有用例的东西。
这样做的缺点是人们错误输入变量不会看到
错误。 我们可以通过警告解决这个问题,但如果你这样做了
目的你不想要警告..这是我唯一的问题
建议。

这在定义主题以减小生成的 CSS 的大小时尤其有用。 通常在创建主题时,希望启用设置很多属性。 但是,用户可能只想设置其中一些属性,而其余属性未设置。 当然,将它们设置为默认值通常会起作用,但是,这会使最终的 CSS 膨胀。

考虑以下基本主题(模板):

.my-class {
    color: @text-color;
    background-color: @background-color;
    border-color: @border-color;
}

主题的用户可能只想设置文本颜色而不设置其余属性。 当然可以将值设置为“初始”、“透明”、“继承”等,但是,这会增加最终 CSS 的大小。 主题往往有数百个这样的属性,所以这个大小可能会大大增加。

@JechoJekov

我认为您错过了此功能请求的要点,请注意,对于您的用例,您仍然需要对这些属性中的每一个进行保护(因此我在上面建议的两篇文章的基本原理仍然适用)。 因此,我认为您的用例不会为 #1400 添加任何新功能(基本上,您似乎在建议一个不同的功能,例如“如果属性的所有值都设置有未定义的变量,则跳过属性”或类似的东西,但这是另一个大故事)。

@七相最大

如果可以将变量设置为“未定义”或“未设置”值,也许最好。 将属性设置为这样的值意味着该属性不会在最终的 CSS 中呈现。 这简化了语法并可以验证变量是否已声明。

@JechoJekov

如果可以将变量设置为“未定义”或“未设置”值,也许最好。

正如我所提到的,这是另一个需要自己的票的功能(有自己的讨论)。

只是对路线图的一些澄清:这被标记为“ReadyForImplementation”,但在这个线程中似乎没有达成共识,看起来@seven-phases-max 提出了一个不错的选择。 @lukeapage @seven-phases-max,是否应该删除已准备好实施?

@matthew-dean 不知道。 我想这里的“ReadyForImplementation”仍然是有效的,意思是“这个功能似乎不是真的需要,但如果有人想出一个 PR 来实现这样的东西,就不会有阻力”或某种:)

怎么样? (在考虑中)

对我来说,“为实施做好准备”意味着普遍(但不一定是一致)的共识是它_应该_被解决或实施。 我认为“考虑中”符合您的定义。

使用范围时仍然遇到这个问题

<strong i="6">@foo</strong>: 'bar';
& { 
  // not sure if <strong i="7">@foo</strong> is defined
}

希望能够使用isDefined

虽然像is-defined这样的东西是有意义的(作为语法糖),但它并不是真正需要的。 _always_ 定义@foo (使用您需要的任何默认值),毕竟如果您在样式中使用变量 - 您_可以_ 预测您需要定义它,例如:

// in a far far away galaxy of your library default variables:
<strong i="8">@foo</strong>: undefined;

// the code where you need it
& when not(<strong i="9">@foo</strong> = undefined) { 
    // <strong i="10">@foo</strong> is defined by user    
}

// some user code
<strong i="11">@foo</strong>: bar;

虽然我想我已经在上面的例子中写过这个(我只是想确保表达这样一个事实,即缺乏这个功能不能成为任何事情的阻碍。如果你知道它所在的任何用例一个,请分享)。

我正在尝试在浏览器中设置主题预览。

这意味着我必须使用less.js并使用运行时设置覆盖默认的@theme变量。

window.less.modifyVars({
  theme: 'someOtherValue'
})

在我的 LESS 中,我正在导入一个全局theme.less文件,其中包括

<strong i="14">@theme</strong>: undefined;

.setTheme {
  <strong i="15">@theme</strong> : 'default';
}
.setTheme;

我也试过了

<strong i="19">@theme</strong>: 'default';

在第一种情况下,输出值是undefined在第二种情况下,无论modifyVars中设置了什么,它始终是default #$

单击此处的主题下拉列表以获取示例。

当前的解决方法(在实时站点上)是手动获取变量导入路径,并使用正则表达式对其进行解析,然后使用modifyVars导入所有这些变量,您可以猜到这很混乱

@seven-phases-max 我已将其缩小到一个非常具体的测试用例,其中@import会导致问题。

在这两种情况下都假设:

Javascript

window.less.modifyVars({
  theme: 'changedValue'
});

在 theme.less 的第一种情况下, outer正确设置为changedValue

无组件

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

无主题

<strong i="22">@theme</strong>: 'default';
.outer {
  value: @theme; // value is set to changedValue
}

在这种失败的情况下,在 theme.less 中@theme设置为default而不是changedValue

无组件

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

无主题

<strong i="37">@theme</strong>: 'default';
<strong i="38">@import</strong> "@{theme}"; // theme is set to default not changedValue

如果是 "var in mixin",代码片段变成:

// defaults:
.setTheme {
   <strong i="6">@theme</strong>: undefined;
}

// custom:
.setTheme {
  <strong i="7">@theme</strong>: 'default';
}
.setTheme;

虽然对于modifyVars它应该总是为您尝试的任何一个片段生成'someOtherValue' (因为modifyVars只需在编译的末尾附加普通的<strong i="14">@theme</strong>: 'someOtherValue';行即可源代码)。 因此,如果modifyVars不起作用,则问题很可能出在其他地方。
我确实查看了button页面代码,但它太复杂了,无法快速说出可能出错的地方。 快速浏览一下,我看不到(或者可能只是无法理解)如何重新编译 Less 代码,并在modifyVars之后重新应用生成的 CSS(在简单的片段中,它是通过less.refreshStyles之后的modifyVars完成的


顺便说一句,以防万一,您确定您不打算将is-default用作以下内容:

.setTheme when not(is-defined(@theme)) {
  <strong i="26">@theme</strong>: 'default';
}
.setTheme;

? 如果是这样,问题是这样的代码将直接违反惰性评估原则(如果变量未定义, is-defined应该返回 false,但由于它_将_立即在该范围内定义, is-defined也有返回 true -> 无法解决的递归。(有关为什么不能安全地允许条件命令式重定义的更多详细信息,请参阅 #2072)。
即它实际上可以按预期工作(除非is-defined被编码为显式检测和拯救这种递归)但仅作为一种未指定/未定义的行为,与正常的变量可见性规则没有一致性,因此创建了甚至更乱。

@jlukic

<strong i="8">@theme</strong>: 'default';
<strong i="9">@import</strong> "@{theme}"; // theme is set to default not changedValue

是的,这也是我的怀疑之一(不幸的是,我现在无法对此进行测试,我也无法猜测这是如何发生的,但是是的,这是可能的,因为在导入中使用变量是真正的黑魔法。我将创建一个专门的如果这是问题,请发布报告)。

我相当肯定这与那美妙的黑魔法有关。

我一直在使用非常简单的较少文件进行调试,但是我还没有机会创建测试用例存储库。

在我的测试中,失败是特定于在导入中使用{@variable}值,而不是在 css 属性中。

真正享受更少的主题,很想克服这些问题。 这是我上周在 Meteor DevShop 上展示的 LESS 的一个有趣的主题示例(单击右上角的油漆桶图标)

@jlukic我对其进行了测试,并且modifyVars按预期适用于导入中的变量lesscless.js (请参阅codepen demo ,那里的代码有点棘手,因为它必须从gist可怕的 url 导入,但我想它等于你的页面应该执行的操作......所以实际上问题似乎出在其他地方)。

感谢您花时间为我制作测试用例。 这是非常明确的。 我不确定我发生了什么事..我必须调查

这个请求有什么动静吗? 一段时间以来,我一直担心未定义的变量检查。 目前,我有一个远非理想的工作......

p {
  & when not (@body-text-color = null) {
    color: @body-text-color;
  }
}

但是,要使其正常工作,该变量必须存在并且默认定义为null 。 如果我可以首先检查变量是否存在,这将更加有效/灵活。

+1

总而言之:在插件中实现is-defined(name) (和/或get-value-of(var-name, fallback-value-if-not-defined) )函数需要少于 15 行代码。 所以,只要你足够勇敢,就这样做吧。

对此+1。 作为插件有什么进展吗? 我的意思是,@seven-phases-max 你比大多数贡献者更了解代码,我猜。 如果它需要大约 15 行代码,为什么还没有完成呢?

如果它需要大约 15 行代码,为什么还没有完成呢?

因为没有人真正感兴趣? 当其他人为您执行此操作时,每个功能都很酷且“非常有用”......但是当它是关于您自己的时间时,它神奇地变得不那么重要;)

你比大多数贡献者更了解代码

这并不意味着我必须将自己感兴趣的任何事情放在其他人感兴趣的事情之后,对不起(特别是如果这是鼓励我个人认为是滥用/错误代码风格的事情)。

对于@ashenden的用例,不要听起来很空洞:这是使您的任何规则集可以通过“未知数”进行自定义的正确方法(显然假设由于某种原因普通CSS覆盖不适用的情况)而没有缺陷“盲目移动每一个 CSS 值到变量加使用你不使用的东西”的想法(我不会详细说明为什么它实际上被破坏了,因为它是一个故事长篇博文)。

有一个概念通常被命名为“钩子”,所以下面的“can't make my mind up Festival”:

// .............................................
// base code:

p {
    & when not (is-defined(@body-text-color)) {
        color: @body-text-color;
    }
    & when not (is-defined(@body-text-color)) {
        background-color: @body-back-color;
    }
}

span {
    & when not (is-defined(@body-text-color)) {
        color: @body-text-color;
    }
    & when not (is-defined(@body-text-color)) {
        background-color: @body-back-color;
    }
}

// .............................................
// customization:

@body-back-color: blue; // how about gradient?

应替换为:

// .............................................
// base code:

p {
    .body-text();
    .body-back();
    // ^ it's actually better to group all this into a singe entity, e.g. .p()
    // so that as you don't know WHAT or HOW to be customized
    // you don't pre-enforce any limitations and/or hardcoded approach
    // for something YOU do not even write
}

span {
    .body-text();
    .body-back();
}

.body-text() {}
.body-back() {}

// .............................................
// customization:

.body-back() {background-color: blue}

数数行数,数数可能性,数数限制。
即真的,如果您不使用某些 CSS 属性,请将它们单独留给那些将使用 .

伙计们,这就是我遇到这个问题的地方,请看一下这个例子,告诉我它是否是一个有效的案例,如果没有 is-undefined 就无法修复。

我想在像这样的模块的库中加载主题:

在我的核心应用程序中,我定义了变量@theme : 'yellow',然后少导入库。

在这种情况下,这就是 library less 的样子:

@import '主题/@{主题}';

身体 {
背景:@primaryColor;
}

这样我就可以使用@primaryColor : yellow 和@primaryColor : red 来获得 yel​​low.less 和 default.less 。

最后,我可以使用诸如primaryColor之类的语义变量名称来编写我的库,并提供一组定义了这些变量的主题。 库用户只需在他的应用程序中定义主题名称并导入库样式。

好吧,没关系,看起来我明白了,我只需要一个像这样的导入 mixin:

<strong i="6">@theme</strong>: 'default';

.imports(@theme) when (<strong i="7">@theme</strong> = 'yellow') {
    <strong i="8">@import</strong> "themes/yellow";
}
.imports(@theme) when (<strong i="9">@theme</strong> = 'default') {
    <strong i="10">@import</strong> "themes/default";
}
.imports(@theme);

@waterplea您可以将代码简化为:

<strong i="7">@theme</strong>: default;

.imports(yellow) {<strong i="8">@import</strong> "themes/yellow";}
.imports(default) {<strong i="9">@import</strong> "themes/default";}
.imports(@theme);

另请注意已删除的多余引号。

虽然老实说,我看不出<strong i="13">@theme</strong>: yellow; (+所有混合代码)比明确的<strong i="15">@import</strong> "themes/yellow";行更好。 即首先你知道压倒一切吗? 即你不需要隐藏默认<strong i="18">@import</strong> "themes/default"; ,如果你的图书馆用户需要应用yellow东西 - 她只是导入你的黄色主题(在你的主库文件之后的任何地方,又是同一行)和voilà - 您的库使用黄色文件中指定的变量值。

我最终选择了一个覆盖主题或切换到预设主题的文件的可选导入。 在使用该库的项目中,如果项目需要不同的主题,我们然后将 webpack 配置为使用该文件的别名作为节点模块。 我之前写的或者你写的在我们的案例中是行不通的,因为我们正在为 Angular 构建一个 UI 工具包并且它是高度模块化的。 我们可以只导入一个按钮或选择控件,它必须知道我们使用的主题及其所有样式封装。 该解决方案易于设置,因此我很满意。

您写的内容在我们的案例中不起作用,因为我们正在为 Angular 构建一个 UI 工具包,并且它是高度模块化的。

我以前已经无数次看到过这种情况-这是最可悲的少(错误)理解问题之一-人们只是错过了基本的少懒惰评估原则,而不是使用自然的少覆盖,而是使用大量文件注入构建库-基于自定义模式的灵感来自具有直接相反变量语义的语言(如 PHP、Sass 等)的习惯。 这肯定不仅仅是某个人不断尖叫“伙计们,你做错了!”

我很想把它做对,我并不是说我理解一切:) 愿意解释你对我的例子的建议吗? 这是我们得到的:

这是一个最小的结构示例:

图书馆:

  • 导入.less
    <strong i="10">@import</strong> 'theme-default';
    <strong i="13">@import</strong> 'mixins';
  • mixins.less
    some mixins here like resetting default browser button styles
  • 主题-default.less
    <strong i="20">@primary</strong>: red;
    <strong i="23">@secondary</strong>: blue;
  • 主题-secondary.less
    <strong i="27">@primary</strong>: green;
    <strong i="30">@secondary</strong>: yellow;
  • 按钮组件
    <strong i="34">@import</strong> 'import';
    .button { color: @primary; }

项目 1(必须使用默认主题):

  • 零件
    <strong i="42">@import</strong> 'import';
    body { background: @secondary; }

项目2(必须使用次要主题):

  • 零件
    <strong i="50">@import</strong> 'import';
    body { background: @secondary; }

我们的库被添加为节点模块,在项目中我们只从库中导入按钮组件。 由于它是该按钮的 Angular 模块,它拥有自己的 less 文件,因此它与项目 1 或项目 2 的其余部分分开编译。

我一直在考虑这个问题,但我想不出一种方法来组织更少的文件以允许项目 1 或项目 2 在编译组件和项目自己的代码时设置要使用的主题文件。 我看不到从 Project 代码中覆盖按钮组件样式的方法。

因此,基本上,这就是我编写 import.less 的方式:
<strong i="58">@import</strong> 'theme-default';
<strong i="61">@import</strong> 'mixins';
<strong i="64">@import</strong> (optional) '~custom-theme';

在项目中,如果我想覆盖项目代码和库组件的主题变量,我使用 webpack 将此文件别名为模块。 该文件可以明确覆盖@primary或只是切换到次要主题:
<strong i="69">@import</strong> '~myLibrary/styles/theme-secondary';

所以我知道覆盖,问题是 - 库的用户无法在“主库文件之后的任何位置”和库组件代码之间进入。 我知道您所说的内容适用于没有样式封装的框架,但对于 Angular,它要么不起作用,要么我误解了您的解决方案中的某些内容。

我知道了。 然后我们实际上在谈论同一件事(只是用不同的词)。 只要buttonComponent (或任何其他组件)单独编译,“项目自定义主文件”仍然是import.less文件,您完全按照我的建议进行了操作。 (而Project 1Project 2变成与buttonComponent相同级别的另一个“任何其他”(或“一体式”?)组件,而不是相当“项目”)。

即这样我会说你“做对了”符合我的口味。

此问题已自动标记为过时,因为它最近没有活动。 如果没有进一步的活动发生,它将被关闭。 感谢你的贡献。

@stale平。

从技术上讲,实现该功能的插件自 2015 年以来可用。 但就我个人而言,我从未测试过它,所以如果出现问题请不要责怪我(仅供参考)。

此问题已自动标记为过时,因为它最近没有活动。 如果没有进一步的活动发生,它将被关闭。 感谢你的贡献。

所以......我浏览了这个线程,我无法确定这是否主要是人们误解了 Less scope 的情况。

我真的不需要检查是否定义了变量。 如果要消耗变量,则应定义变量。

<strong i="7">@import</strong> "library";  // contains @library-color: blue;

@library-color: red;

.box {
  color: @library-color;
}

我不明白需要根据值_changed_ 有条件地设置属性。 如果属性设置为变量,只需更改变量的值。

也就是说,如果library.less有这个:

@library-color: blue;
.box {
  color: @library-color;
}

设置其输出所需要做的就是:

<strong i="16">@import</strong> "library";
@library-color: red;

这将输出:

.box {
  color: red;
}

该线程中的 OP 和其他人是否理解这种行为?

我认为“是否存在 var”的用例是一种做标志的好方法。 也就是说,我认为没有概念上直截​​了当的“虚假”。 或者更确切地说, true是唯一的“真实”值是不寻常的。 换句话说,我认为这是教育问题,而不是行为问题。

我认为我们应该考虑关闭这个问题,因为这通常通过声明默认变量值的一行来解决。 尽管 Less v3.5 朝着更宽松的交易方向发展,但可能对后卫内部的变量进行更宽松的评估?

不过,我的投票是否定的。 @the-variable: false;似乎是所有 OP 需要添加的。


对于任何对如何解决他们认为与此相关的特定问题有疑问的人,请随时在less gitter上发帖并@我。

好的结束,谢谢@calvinjuarez。

自从它打开以来,还发生了if()函数。 所以你可以做color: if((@variable), green, red);

@matthew-dean 非常感谢您写下这个提示。 我在 3.0 发行说明中看到了 if() 的添加,但没有与这个问题建立联系,我有一个独特的案例(但不是制作缩减版本的时间)。 再次,非常感谢。 干得好,少队。

@kbav没问题! 该提示之上的另一个提示:当首次引入if()时,它需要在条件周围加上括号,因为它基本上是“嵌入”了一个 when 守卫(例如,$# 中when (@variable) when之后的部分when (@variable) )。 但是,这已在 Less 3.6 中得到修复,因此您可以在不带括号的情况下编写上面的示例(如果使用最新版本编译):

color: if(<strong i="10">@variable</strong>, green, red);
此页面是否有帮助?
0 / 5 - 0 等级