scss: map:merge 和 @each $attribute, $value in $variables
研究 Element-plus 源码时,有以下一段代码:
// path: element-plus/packages/theme-chalk/src/button.scss
@include b(button) {
@include set-component-css-var('button', $button);
}
有关 b 的理解,以及 @include 的作用
set-component-css-var
// path: element-plus/packages/theme-chalk/src/mixins/_var.scss
@mixin set-component-css-var($name, $variables) {
@each $attribute, $value in $variables {
@if $attribute == 'default' {
#{getCssVarName($name)}: #{$value};
} @else {
#{getCssVarName($name, $attribute)}: #{$value};
} }}
这段代码定义了一个 Sass 混合名为 set-component-css-var
,它有两个参数:$name
和 $variables
。$name
参数是组件的名称,$variables
参数是一个 CSS 变量名到值的映射。
混合名会遍历 $variables
映射中的每个键值对。如果键等于 "default"
,那么混合名会将组件的默认 CSS 变量设置为值。否则,混合名会将组件的特定属性的 CSS 变量设置为值。
getCssVarName
函数用于生成 CSS 变量名。函数有两个参数:组件名称和可选的属性名称。如果未提供属性名称,则函数会返回组件的默认 CSS 变量名(例如,--button
)。否则,函数会返回包含组件名称和属性名称的 CSS 变量名(例如,--button-color
)。
以下是一个使用 set-component-css-var
混合名的示例:
@mixin set-component-css-var($name, $variables) {
@each $attribute, $value in $variables {
@if $attribute == 'default' {
#{getCssVarName($name)}: #{$value};
} @else {
#{getCssVarName($name, $attribute)}: #{$value};
}
}
}
$button-variables = (
"default": "#fff",
"color": "#000",
"border-color": "#ddd",
);
@include set-component-css-var("button", $button-variables);
上边代码生成什么了?
--button: #fff;
--button-color: #000;
--button-border-color: #ddd;
这段代码的用途是将 CSS 变量与组件关联起来。例如,我们可以使用这段代码为按钮组件设置默认颜色、文字颜色和边框颜色。
具体来说,这段代码的执行步骤如下:
- 混合名会遍历
$variables
映射中的每个键值对。 - 如果键等于
"default"
,那么混合名会将组件的默认 CSS 变量设置为值。 - 否则,混合名会将组件的特定属性的 CSS 变量设置为值。
在示例代码中,$button-variables
映射包含三个键值对:
"default"
的值为#fff
,表示按钮组件的默认颜色。"color"
的值为#000
,表示按钮组件的文字颜色。"border-color"
的值为#ddd
,表示按钮组件的边框颜色。
因此,这段代码会将以下 CSS 变量设置为相应的值:
--button
的值为#fff
。--button-color
的值为#000
。--button-border-color
的值为#ddd
。
sass 中 map 的用法
scss: map:merge 和 @each $attribute, $value in v a r i a b l e s 上边的仅仅是一个简单的示例。看源码过程中,我们可以找到 ‘ variables 上边的仅仅是一个简单的示例。看源码过程中,我们可以找到 ` variables上边的仅仅是一个简单的示例。看源码过程中,我们可以找到‘button` 的定义:
// path: element-plus/packages/theme-chalk/src/common/var.scss
$button: () !default;
$button: map.merge(
(
'font-weight': getCssVar('font-weight-primary'),
'border-color': getCssVar('border-color'),
'bg-color': getCssVar('fill-color', 'blank'),
'text-color': getCssVar('text-color', 'regular'),
'disabled-text-color': getCssVar('disabled-text-color'),
'disabled-bg-color': getCssVar('fill-color', 'blank'),
'disabled-border-color': getCssVar('border-color-light'),
'divide-border-color': rgba($color-white, 0.5),
'hover-text-color': getCssVar('color-primary'),
'hover-bg-color': getCssVar('color-primary', 'light-9'),
'hover-border-color': getCssVar('color-primary-light-7'),
'active-text-color': getCssVar('button-hover-text-color'),
'active-border-color': getCssVar('color-primary'),
'active-bg-color': getCssVar('button', 'hover-bg-color'),
'outline-color': getCssVar('color-primary', 'light-5'),
'hover-link-text-color': getCssVar('color-info'),
'active-color': getCssVar('text-color', 'primary'),
),
$button
);
注意:要用 map 功能,你需要导入这个功能。以上代码片段第一行就是导入 map 函数
1. 定义变量:
- 代码首先定义了一个名为
$button
的变量,并将其默认值设置为空映射(() !default;
)。这意味着如果$button
变量此前没有被定义,它将会被初始化为一个空映射。
2. 合并映射:
-
代码接着使用了 Sass 内置的
map.merge
函数来合并两个映射:
- 第一个映射包含了一系列按钮样式属性的键值对,例如:
font-weight
: 字体粗细border-color
: 边框颜色bg-color
: 背景颜色text-color
: 文字颜色disabled-text-color
: 禁用状态下的文字颜色hover-text-color
: 鼠标悬停状态下的文字颜色active-text-color
: 鼠标按下状态下的文字颜色- … 等等
- 这些键值对中的值都是通过
getCssVar
函数获取的 CSS 变量,这意味着这些样式属性的值可以被集中控制和调整。 - 第二个映射是
$button
变量本身。如果$button
变量已经被定义并包含了一些样式属性,那么这些属性将会被保留并与第一个映射中的属性进行合并。
- 第一个映射包含了一系列按钮样式属性的键值对,例如:
3. 最终结果:
- 经过
map.merge
函数的处理,$button
变量将会包含一个完整的按钮样式属性映射,其中的值都是 CSS 变量。 - 这意味着我们可以通过修改 CSS 变量的值来轻松调整按钮的整体样式,而无需逐一修改每个样式属性。
总结:
- 这段代码的目的在于使用 CSS 变量来定义按钮的样式,使样式管理更加灵活和统一。
- 通过使用
map.merge
函数,代码可以将默认的按钮样式属性与自定义的属性进行合并,保留了灵活性和可扩展性。
getCssVar 和 getCssVarName
在以上代码中用到了 getCssVar
和 getCssVarName
这两个函数,我统一解释一下,因为这两个函数都调用了 joinVarName
// path: element-plus/packages/theme-chalk/src/mixins/function.scss
@function joinVarName($list) {
$name: '--' + config.$namespace;
@each $item in $list {
@if $item != '' {
$name: $name + '-' + $item;
}
}
@return $name;
}
// getCssVarName('button', 'text-color') => '--el-button-text-color'
@function getCssVarName($args...) {
@return joinVarName($args);
}
// getCssVar('button', 'text-color') => var(--el-button-text-color)
@function getCssVar($args...) {
@return var(#{joinVarName($args)});
}
#{}
插值,可以通过#{}
插值语法在选择器和属性名中使用SassScript
变量
解释一下这段代码的作用:
1. joinVarName
函数:
- 这个函数的作用是将一个列表(
$list
)中的所有元素连接起来,并在开头加上一个前缀(--
加上config.$namespace
),形成一个完整的 CSS 变量名。 - 它会遍历
$list
中的每个元素,如果元素不为空,则将其添加到$name
变量中,并在每个元素之间加上一个连字符(-
)。 - 最后,函数会返回生成的 CSS 变量名。
2. getCssVarName
函数:
- 这个函数是
joinVarName
函数的一个简便调用方式。它可以接受任意数量的参数($args...
),并将这些参数作为列表传递给joinVarName
函数。 - 它会返回
joinVarName
函数生成的 CSS 变量名。
举例说明:
- 调用
getCssVarName('button', 'text-color')
会返回--el-button-text-color
(假设config.$namespace
的值为el
)。 - 调用
getCssVarName('primary-color')
会返回--el-primary-color
。
代码的作用:
- 这段代码是为了方便地生成 CSS 变量名,并在生成的变量名中加入命名空间(
config.$namespace
)以避免变量名冲突。 - 这在大型项目中,尤其是使用了 CSS 预处理器的项目中,是非常有用的,可以帮助我们更好地管理 CSS 变量。
关于 getCssVar 解释两个点:$args… 和 var
在 Sass 中,$args...
表示一个可变参数列表。这意味着 args
变量可以接受任意数量的参数,这些参数可以是任意类型的值。
在 getCssVar
函数中,$args...
用于接收 CSS 变量名的参数列表。例如,调用 getCssVar('primary-color')
函数时,$args...
将包含一个元素,该元素的值为 'primary-color'
。
$args...
的使用可以使函数更加灵活,可以接受任意数量的参数。
以下是一些使用 $args...
的例子:
@function add($x, $y...) {
$total: $x;
@each $arg in $y {
$total: $total + $arg;
}
@return $total;
}
// 将两个数相加
$sum: add(1, 2, 3);
// 将三个字符串连接起来
$string: add('a', 'b', 'c');
在第一个示例中,add()
函数用于将两个或多个数相加。$args...
用于接收任意数量的数字参数。
在第二个示例中,add()
函数用于将三个字符串连接起来。$args...
用于接收任意数量的字符串参数。
为什么这里要解释一下 var ?
var 函数容易有歧义,因为在 css 中和 sass 中都有这个函数,他们的功能也是不一样的,因此在这做一个区分:简单来说,sass 中的 var 函数用来定义变量,CSS 中的 var 用来引用变量。
CSS 中的 var
在 CSS 中,var()
函数是 CSS custom properties 的语法,用于引用 CSS custom properties。
CSS custom properties 是 CSS 中的一个新特性,允许我们在 CSS 中定义自定义变量。
CSS custom properties 的语法如下:
--[variable-name]: [value];
例如,以下 CSS 代码定义了一个名为 --color
的 CSS custom property,并将其值设置为 #fff
:
--color: #fff;
我们可以使用 var()
函数引用 CSS custom properties。例如,以下 CSS 代码将 .my-element
元素的颜色设置为 --color
变量的值:
.my-element {
color: var(--color);
}
在 Sass 中,var()
函数用于定义 CSS custom properties。因此,Sass 中的 var()
函数与 CSS 中的 var()
函数是相同的。
sass 中的 var
在 Sass 中,var()
函数用于定义 CSS 变量。
var()
函数的语法如下:
var($name, $value);
$name
是变量的名称,$value
是变量的值。
var()
函数会将 $name
变量定义为一个 CSS 变量,并将 $value
赋值给该变量。
以下是使用 var()
函数的例子:
$color: var(--color);
这段代码会定义一个名为 $color
的 CSS 变量,并将其值设置为 var(--color)
。
$color
变量可以被用于 CSS 中,例如:
.my-element {
color: $color;
}
这段 CSS 将会将 .my-element
元素的颜色设置为 var(--color)
变量的值。
var()
函数还可以用于定义具有默认值的 CSS 变量。例如:
$color: var(--color, #fff);
这段代码会定义一个名为 $color
的 CSS 变量,并将其值设置为 #fff
。如果 var(--color)
变量未定义,则将使用 #fff
作为其值。
var()
函数是 Sass 中定义 CSS 变量的重要工具。它可以帮助我们在 CSS 中实现灵活和可重用的样式。
@mixin与@include %与@extend @function
@mixin 使用@include引用,可传参,返回一段css样式,不用会生成一个class,生成结果是直接拷贝,这样多处引用,会造成代码重复
% 使用@extend引用,不可传参,返回一段css样式。%开头的class只是用来extend,不会生成在代码里。生成后,多个class用逗号连接,提取成一个样式,减少重复代码.
@function与@mixin的最大不同,返回的是一个值,不是一段css样式
@forward与@use
@forward:
概念:@forward用于将模块的部分或全部内容重新导出,使其他模块可以通过@use导入该模块的内容。
@use:
概念:@use用于导入其他Scss或Sass模块,并创建一个命名空间来管理导入的模块中的变量、混合器、函数和样式规则。
SCSS中关于@use和@import的区别
区别
功能/特性 | @import | @use |
---|---|---|
作用域 | 共享同一个作用域 | 创建命名空间,具有隔离性 |
重复加载 | 可能导致重复加载 | 保证每个模块只加载一次 |
推荐版本 | 较旧版本的导入方式 | 新版本 Sass 推荐的导入方式 |
命名空间 | 无 | 可以自定义命名空间 |
模块化支持 | 较弱 | 提供更好的模块化支持 |
性能 | 可能存在性能问题 | 更优化的性能 |
避免全局污染 | 不提供隔离性 | 提供隔离性 |
导入方式 | 使用 @import 导入 | 使用 @use 导入 |
上面表格就列出了二者的区别,然后我们来重点看一下重复加载和命名空间。
重复加载
当使用 @import
导入模块时,如果在多个文件中多次导入同一个文件,可能会导致重复加载的问题。这意味着被导入的文件将在每个使用了 @import
的文件中都被加载一次,导致样式表中包含多份相同的样式,从而影响性能和增加文件大小。
让我们通过一个示例来说明重复加载的问题:
假设有以下两个 SCSS 文件:
文件:_variables.scss
scss复制代码// _variables.scss
$primary-color: #007bff;
$secondary-color: #6c757d;
文件:styles1.scss
scss复制代码// styles1.scss
@import 'variables';
body {
background-color: $primary-color;
}
文件:styles2.scss
scss复制代码// styles2.scss
@import 'variables';
button {
background-color: $secondary-color;
}
在这个示例中,我们有两个样式文件 styles1.scss
和 styles2.scss
,它们分别使用 @import
导入了同一个 _variables.scss
文件。由于 styles1.scss
和 styles2.scss
都导入了 _variables.scss
,在编译这两个样式文件时,_variables.scss
将被加载两次。
编译后的结果如下所示:
编译后的 styles1.css
:
css复制代码/* _variables.scss 中的内容被导入到这里 */
body {
background-color: #007bff;
}
编译后的 styles2.css
:
css复制代码/* _variables.scss 中的内容被导入到这里 */
button {
background-color: #6c757d;
}
可以看到,styles1.css
和 styles2.css
中都包含了 _variables.scss
中定义的样式,而实际上 _variables.scss
中的内容只需要加载一次,然后在多个样式文件中共享。重复加载会增加 CSS 文件的大小,影响性能,并可能导致样式冲突问题。
而使用 @use
导入方式可以避免重复加载问题,因为它会确保每个模块只加载一次,即使在多个文件中导入。这样可以优化性能,并保持样式表的精简和一致性。
命名空间
@import是没有命名空间的,我们来介绍一下@use的命名空间是如何作用的。
1. 不使用as,则直接将文件名当作命名空间
在 SCSS 中,当在 @use
后面直接跟上文件路径,并且没有使用 as
关键字指定命名空间,表示将导入的模块内容整体作为一个命名空间,并且使用被导入文件的名称作为命名空间的标识。
假设有以下两个 SCSS 文件:
文件:_variables.scss
scss复制代码// _variables.scss
$primary-color: #007bff;
$secondary-color: #6c757d;
文件:styles.scss
scss复制代码// styles.scss
@use 'variables.scss';
body {
background-color: variables.$primary-color;
}
button {
background-color: variables.$secondary-color;
}
在这个示例中,styles.scss
使用 @use
直接导入了 variables.scss
文件,而没有使用 as
关键字指定命名空间。这意味着 _variables.scss
中的所有内容将被合并到 styles.scss
中,并且使用 variables
作为命名空间的标识。
注意,因为 @use
创建了命名空间,所以我们在 styles.scss
中需要使用 variables.$primary-color
和 variables.$secondary-color
来访问 _variables.scss
中定义的变量。
2. 使用as xxx,则以后面命名的变量xxx作为命名空间
还是用刚刚的例子,假设有以下两个 SCSS 文件:
文件:_variables.scss
scss复制代码// _variables.scss
$primary-color: #007bff;
$secondary-color: #6c757d;
文件:styles.scss
scss复制代码// styles.scss
@use 'variables.scss' as customVars;
body {
background-color: customVars.$primary-color;
}
button {
background-color: customVars.$secondary-color;
}
在这个示例中,styles.scss
使用 @use
导入了 _variables.scss
文件,并使用 as customVars
为导入的模块创建了一个命名空间 customVars
。然后,我们使用 +
后面加上 customVars
来指定变量名作为命名空间的标识。
在 styles.scss
中,我们可以通过 customVars.$primary-color
和 customVars.$secondary-color
来访问 _variables.scss
中定义的变量。这样的用法可以避免变量冲突,并提供更清晰的命名空间标识,增加代码的可读性。
但是有一种特殊情况,就是通过 as * 来导入
如果在 @use
后面使用 as *
,表示将导入的模块的所有内容直接合并到当前文件中,并且不会创建一个命名空间。这样可以让导入的模块的所有变量、mixin、函数等直接在当前文件中使用,而不需要使用命名空间来访问它们。
让我们继续使用上面的示例:
文件:_variables.scss
scss复制代码// _variables.scss
$primary-color: #007bff;
$secondary-color: #6c757d;
文件:styles.scss
scss复制代码// styles.scss
@use 'variables.scss' as *;
body {
background-color: $primary-color;
}
button {
background-color: $secondary-color;
}
在这个示例中,styles.scss
使用 @use
导入了 _variables.scss
文件,并使用 as *
表示导入模块的所有内容,而不为它们创建一个命名空间。
结尾
总结下来就是,推荐使用 @use
来导入模块,以获得更好的模块化支持、性能优化和避免全局污染问题。而 @import
在新版本 Sass 中已不再推荐使用,并且未来可能会被废弃。