我正在使用一个使用Bootstrap 3的Web应用程序.我有一个基本的3层覆盖架构,其中1)Bootstrap的_variables.scss包含核心变量,2)_app-variables.scss包含覆盖Bootstrap的基本app变量_variables.scss和3)_client-variables.scss包含覆盖_app-variables.scss的特定于客户端的自定义. #2或#3(或两者)都可以是空白文件.所以这是覆盖顺序:
_variables.scss // Bootstrap's core _app-variables.scss // App base _client-variables.scss // Client-specific
在理论上足够简单,但由于我称之为“变量依赖”而出现问题 – 变量被定义为其他变量.例如:
$brand: blue; $text: $brand;
现在,假设上述变量在_variables.scss中定义.然后让我们说在_app-variables.scss中,我只覆盖$brand变量使其变为红色:$brand:red.由于SASS按顺序逐行解释代码,它首先将$brand设置为蓝色,然后将$text设置为蓝色(因为$brand在此时为蓝色),最后将$brand设置为红色.因此最终结果是,之后更改$品牌不会影响基于$brand旧值的任何变量:
_variables.scss --------------------- $brand: blue; $text: $brand; // $text = blue . . . _app-variables.scss --------------------- $brand: red; // this does not affect $text,b/c $text was already set to blue above.
但显然这不是我想要的 – 我希望我的$品牌改变会影响所有依赖它的东西.为了正确地覆盖变量,我现在只是将_variables.scss的完整副本复制到_app-variables.scss中,然后在_app-variables中进行修改.同样地,我将_app-variables.scss的完整副本复制到_client-variables.scss中,然后在_client-variables.scss中进行修改.从维护的角度来看,这显然不太理想(轻描淡写) – 每当我对_variables.scss(在Bootstrap升级的情况下)或_app-variables.scss进行修改时,我都必须手动将更改下来文件覆盖堆栈.而且我必须重新宣布一些我甚至可能无法克服的变量.
我发现LESS有他们所谓的“延迟加载”(http://lesscss.org/features/#variables-feature-lazy-loading),其中变量的最后一个定义在任何地方使用,甚至在最后一个定义之前.我相信这可以解决我的问题.但有没有人知道使用SASS的适当的变量覆盖解决方案?
附录:
这是我已经考虑过的一种技术:以相反的顺序包含文件,使用所有变量的默认值(这种技术也在this question的答案中提出).所以这是如何发挥作用:
_app-variables.scss --------------------- $brand: red !default; // $brand is set to red here,overriding _variables.scss's blue. . . . _variables.scss --------------------- $brand: blue !default; // brand already set in _app-variables.scss,so not overridden here. $text: $brand !default; // $text = red (desired behavior)
所以解决方案几乎是完美的.但是,现在在我的覆盖文件中,我无法访问Bootstrap的_variables.scss中定义的变量,如果我想使用其他Bootstrap变量来定义我的变量覆盖(或我自己的其他自定义变量),我将需要它.例如,我可能想要这样做:$custom-var:$grid-gutter-width / 2;
解决方法
> sassc --version sassc: 3.2.1 libsass: 3.2.5 sass2scss: 1.0.3
我们将使用简化的环境,因此文件名与Bootstrap不匹配.
挑战
给定一个我们无法控制的框架(例如,仅在Continuous Integration环境中安装,在我们的机器中不可用),它以下列方式表示SCSS变量:
// bootstrap/_variables.scss $brand-primary: #f00 !default; $brand-warning: #f50 !default; $link-color: $brand-primary !default;
并在同一框架中给出一个使用变量的文件:
// boostrap/main.scss a:link,a:visited { color: $link-color; }
挑战是:
Include the framework in your own application’s SCSS in such a way that
- variables’ dependencies in the framework are preserved and honores;
- you can depend in on the default values but still be able to change the results on the framework dependencies.
更确切地说:
Include the framework in your application’s SCSS in such a way that
$brand-color
will always be the inverse of$brand-warning
,whatever its value is in the framework.
解
主文件看起来像这样:
// application.scss @import "variables"; @import "bootstrap/variables"; @import "bootstrap/main";
你的变量文件看起来像这样:
// _variables.scss %scope { @import "boostrap/variables"; $brand-primary: invert($brand-warning) !global; }
结果:
> sassc main.scss a { color: blue; }
说明
%scope部分不是SCSS的神奇之处,它只是一个名为scope的隐藏类,专门用于以后使用@extend扩展.我们只是用它来创建一个变量范围(因此得名).
在范围内,我们@import框架的变量.因为此时每个变量都没有值,每个变量都被创建并分配其默认值.
但这是噱头.变量不是全局的,而是本地的.我们可以访问它们,但它们不会污染全局范围,后者将用于在框架内派生变量.
事实上,当我们想要定义变量时,我们希望它们是全局变量,实际上我们使用!global关键字来指示SCSS将它们存储在全局范围内.
注意事项
有一个主要的警告:在定义它们时不能使用自己的变量.
这意味着在这个文件中
%scope { @import "boostrap/variables"; $brand-primary: black !global; @debug $brand-primary; }
@debug语句将打印在boostrap / _variables.scss中定义的默认值,而不是黑色.
解
拆分变量分为两部分:
%scope { @import "boostrap/variables"; $brand-primary: black !global; @debug $brand-primary; } @debug $brand-primary;
第二个@debug确实会正确打印黑色.