SASS 多级变量 precedence/inheritance
SASS Multiple levels of variable precedence/inheritance
我正在使用 Laravel Mix 和 Webpack 进行 SASS 预处理。
我的网站上有两个 "themes",我想精简它们,在需要的地方继承变量。例如,我的主要主题将按以下顺序包含:
// Primary theme
@import "./primary-variables.scss";
@import "/path/to/default/theme/main.scss";
我的默认主题是这样的:
// Default theme
@import "./default-variables.scss";
@import "~bootstrap-sass/assets/stylesheets/_bootstrap";
与 this question 类似,我首先包含主要变量,然后是默认主题变量,最后是 bootstrap。
在我的默认主题中,我将 !default
添加到所有变量,因此在重新定义 Bootstrap 的地方,它们将被优先使用,而在新的地方,它们将成为默认值。主要主题根本不使用 !default
。
工作示例
如果 Bootstrap 将 $brand-danger
定义为 red !default
,我的默认主题将其重新定义为 blue !default
,而我的主要主题将其重新定义为 yellow
,我的渲染输出将是黄色的 - 太棒了!
问题
当我需要引用仅在我的主要主题的其他级别定义的变量时。例如:
// Primary theme:
// This fails since I haven't defined $brand-primary in my primary theme
$my-primary-theme-variable: $brand-primary;
构建现在失败并显示错误 primary-theme/src/scss/main.scss doesn't export content
。
解决方法
我可以通过将整个 Bootstrap 变量文件复制到我的主要主题并根据需要更改变量来解决这个问题,但我真的不想这样做。
问题
SASS 变量处理器实际上是如何工作的?我是否可以只更改主题中的 Bootstrap 变量之一而不必重新定义整个文件?
非常相似。
您似乎正在使用 @include 来导入您的 SCSS,请尝试使用 @import – 如果这只是一个错字问题请让我知道:-)
@import "./primary-variables.scss",
"/path/to/default/theme/main.scss"
;
我在您提到的 the question 上添加了一些简短的注释。
关于 !default 标志,重要的是它在选择器中使用时生效,而不是 re-define 变量。
Sass 在处理变量时不向前看——它打印出当前值。在此示例中,.class-1 将是 red,因为 re-definition 在选择器中使用后出现,而 .class-2 将是蓝色,因为没有默认标志。
$brand-color: red !default; // defined
.class-1 { background-color: $brand-color; } // red
$brand-color: blue; // re-defined
.class-2 { background-color: $brand-color; } // blue
默认标志将导致 Sass 跳过变量 re-definition。在这个例子中,结果将是 red 作为第一个定义。由于默认标志,以下两个 re-definition 被忽略。
$brand-color: red !default; // defined
$brand-color: blue !default; // ignored
$brand-color: green !default; // ignored
.class-1 { background-color: $brand-color; } // red
在这种情况下,将使用配置中的所有变量——然后来自 partial-1 的变量(如果未在配置中定义)和最后一个 partial-2 将定义其他两个中未定义的任何变量。
@import '_config.scss'; // definition
@import '_partial-1.scss'; // contains defaults
@import '_partial-2.scss'; // contains defaults
希望它有意义:-)
导入结构
// _default-theme.scss
@import '_default-variables.scss', '_bootstrap.scss';
// _primary-theme.scss
// primary variables will override defaults or use defaults if not defined
@import '_primary-variables.scss', '_default-theme.scss';
// style.scss
@import '_primary-theme.scss'; // or '_default-theme.scss'
范围
如果您的默认和主要内容对于每个主题都是唯一的,您可以创建一个作用域混合来处理编译的内容。
这是一个非常初级的版本:
// _scope.scss
$scope-context: null !default;
@function scope($scopes: null, $true: true, $false: false) {
@each $scope in $scope-context {
@if index($scopes, $scope) { @return $true }
}
@return $false;
}
@mixin scope($scopes: null) {
@if scope($scopes) or length($scopes) == 0 and not $scope-context {
@content;
}
}
工作原理
作用域混合接受一个上下文参数和一个内容块@content。如果传递的上下文与全局变量 ($scope-context) 匹配,则内容块将被渲染。
// _default-theme.scss
.class { content: 'Will show in both themes'; }
@include scope(default-theme){
.class { content: 'Will only show in the default theme'; }
}
@include scope(primary-theme){
.class { content: 'Will only show in the primary theme'; }
}
// can also be used as "if" function
.class {
content: scope(default-theme, 'Is default', 'Not default')
}
在你的例子中,在默认变量和主变量中定义 $scope-context
// _default-variables.scss
$scope-context: default-theme !default;
// _primary-variables.scss
$scope-context: primary-theme;
... 并将 _scope.scss 添加到 _default-theme.scss
// _default-theme.scss
@import '_default-variables.scss', '_bootstrap.scss', '_scope.scss';
我发现的问题是我错误地假设了 SASS 的工作原理。
当你定义一个变量声明时,它的值是在你写的时候编译的。例如,$my-var: $brand-primary
会在处理时将 $brand-primary
的 当前值 分配给 $my-var
。
这意味着我无法实现我想要的,即在 Bootstrap 之上包含一个最小变量文件,因为它只会更新变量本身,而不会更新任何其他变量在 Bootstrap.
中引用该变量
解决方案
不优雅,但是复制每个主题的整个变量文件,并根据需要在每个地方进行调整。
我正在使用 Laravel Mix 和 Webpack 进行 SASS 预处理。
我的网站上有两个 "themes",我想精简它们,在需要的地方继承变量。例如,我的主要主题将按以下顺序包含:
// Primary theme
@import "./primary-variables.scss";
@import "/path/to/default/theme/main.scss";
我的默认主题是这样的:
// Default theme
@import "./default-variables.scss";
@import "~bootstrap-sass/assets/stylesheets/_bootstrap";
与 this question 类似,我首先包含主要变量,然后是默认主题变量,最后是 bootstrap。
在我的默认主题中,我将 !default
添加到所有变量,因此在重新定义 Bootstrap 的地方,它们将被优先使用,而在新的地方,它们将成为默认值。主要主题根本不使用 !default
。
工作示例
如果 Bootstrap 将 $brand-danger
定义为 red !default
,我的默认主题将其重新定义为 blue !default
,而我的主要主题将其重新定义为 yellow
,我的渲染输出将是黄色的 - 太棒了!
问题
当我需要引用仅在我的主要主题的其他级别定义的变量时。例如:
// Primary theme:
// This fails since I haven't defined $brand-primary in my primary theme
$my-primary-theme-variable: $brand-primary;
构建现在失败并显示错误 primary-theme/src/scss/main.scss doesn't export content
。
解决方法
我可以通过将整个 Bootstrap 变量文件复制到我的主要主题并根据需要更改变量来解决这个问题,但我真的不想这样做。
问题
SASS 变量处理器实际上是如何工作的?我是否可以只更改主题中的 Bootstrap 变量之一而不必重新定义整个文件?
您似乎正在使用 @include 来导入您的 SCSS,请尝试使用 @import – 如果这只是一个错字问题请让我知道:-)
@import "./primary-variables.scss",
"/path/to/default/theme/main.scss"
;
我在您提到的 the question 上添加了一些简短的注释。 关于 !default 标志,重要的是它在选择器中使用时生效,而不是 re-define 变量。
Sass 在处理变量时不向前看——它打印出当前值。在此示例中,.class-1 将是 red,因为 re-definition 在选择器中使用后出现,而 .class-2 将是蓝色,因为没有默认标志。
$brand-color: red !default; // defined
.class-1 { background-color: $brand-color; } // red
$brand-color: blue; // re-defined
.class-2 { background-color: $brand-color; } // blue
默认标志将导致 Sass 跳过变量 re-definition。在这个例子中,结果将是 red 作为第一个定义。由于默认标志,以下两个 re-definition 被忽略。
$brand-color: red !default; // defined
$brand-color: blue !default; // ignored
$brand-color: green !default; // ignored
.class-1 { background-color: $brand-color; } // red
在这种情况下,将使用配置中的所有变量——然后来自 partial-1 的变量(如果未在配置中定义)和最后一个 partial-2 将定义其他两个中未定义的任何变量。
@import '_config.scss'; // definition
@import '_partial-1.scss'; // contains defaults
@import '_partial-2.scss'; // contains defaults
希望它有意义:-)
导入结构
// _default-theme.scss
@import '_default-variables.scss', '_bootstrap.scss';
// _primary-theme.scss
// primary variables will override defaults or use defaults if not defined
@import '_primary-variables.scss', '_default-theme.scss';
// style.scss
@import '_primary-theme.scss'; // or '_default-theme.scss'
范围
如果您的默认和主要内容对于每个主题都是唯一的,您可以创建一个作用域混合来处理编译的内容。
这是一个非常初级的版本:
// _scope.scss
$scope-context: null !default;
@function scope($scopes: null, $true: true, $false: false) {
@each $scope in $scope-context {
@if index($scopes, $scope) { @return $true }
}
@return $false;
}
@mixin scope($scopes: null) {
@if scope($scopes) or length($scopes) == 0 and not $scope-context {
@content;
}
}
工作原理
作用域混合接受一个上下文参数和一个内容块@content。如果传递的上下文与全局变量 ($scope-context) 匹配,则内容块将被渲染。
// _default-theme.scss
.class { content: 'Will show in both themes'; }
@include scope(default-theme){
.class { content: 'Will only show in the default theme'; }
}
@include scope(primary-theme){
.class { content: 'Will only show in the primary theme'; }
}
// can also be used as "if" function
.class {
content: scope(default-theme, 'Is default', 'Not default')
}
在你的例子中,在默认变量和主变量中定义 $scope-context
// _default-variables.scss
$scope-context: default-theme !default;
// _primary-variables.scss
$scope-context: primary-theme;
... 并将 _scope.scss 添加到 _default-theme.scss
// _default-theme.scss
@import '_default-variables.scss', '_bootstrap.scss', '_scope.scss';
我发现的问题是我错误地假设了 SASS 的工作原理。
当你定义一个变量声明时,它的值是在你写的时候编译的。例如,$my-var: $brand-primary
会在处理时将 $brand-primary
的 当前值 分配给 $my-var
。
这意味着我无法实现我想要的,即在 Bootstrap 之上包含一个最小变量文件,因为它只会更新变量本身,而不会更新任何其他变量在 Bootstrap.
中引用该变量解决方案
不优雅,但是复制每个主题的整个变量文件,并根据需要在每个地方进行调整。