解决 @import
痛点的替代品
@import
的主要区别虽然 @use
是用来替代 @import
的,但它被设计成了不同的工作方式,这是有意的。以下是两者之间主要的不同点:
@use
仅使变量、函数和混入可以在当前文件范围使用。它不会将这些东西放到全局作用域。这意味着你可以更容易判断这些东西引用自哪里,并且也意味着你能够用更短的变量名而不用担心名称冲突。
@use
对每一个文件都只加载一次。这可以确保不会生成多余的 CSS 代码。
@use
只能出现在文件开头。并且不能写在嵌套的样式规则中。
对于每个 @use
命令,都只能拥有一个引入的 URL。@import
可以引入多个。
@use
不论是使用 sass
还是 scss
语法,其 URL 都必须添加引号。
可以使用 @use
从其它 Sass
样式表中加载混入(mixin)、函数(function)和变量,并且将多个样式表的 css
组合到一起。被 @use
加载的样式表被称作“模块(modules)”。同时 Sass
内部也提供了一系列有用的模块。
最基础的使用方法是 @use "<url>"
,通过 URL 来加载模块。任何通过这种方式加载的样式不论被加载多少次,在编译后输出的 CSS 中只会出现一次,避免了 @import
中多次出现的冗余问题。
{% note warning modern %}
@use
必须放在除了 @forward
以外规则的上方,包括样式规则。但是在配置模块时可以在 @use
前定义变量。
{% endnote %}
{% tabs %}
// foundation/_code.scss
.code {
padding: 0.25em;
line-height: 0;
}
// foundation/_lists.scss
ul,
ol {
text-align: left;
& & {
padding: {
bottom: 0;
left: 0;
}
}
}
// style.scss
@use 'foundation/code';
@use 'foundation/lists';
code {
padding: 0.25em;
line-height: 0;
}
ul,
ol {
text-align: left;
}
ul ul,
ol ol {
padding-bottom: 0;
padding-left: 0;
}
{% endtabs %}
你可以通过 <namespace>.<name>
的形式来使用混入(mixin)、函数(function)和变量。在默认情况下,模块链接名就是 namespace
命名空间。
被加载的成员(变量、函数、混入)只能在加载它们的地方使用。其它样式表需要编写其自身的 @use
规则来使用他们。这有助于判断引用的成员来自哪里。如果你想一次性从多个文件中加载成员,你可以使用 @forward
将需要的成员全部转发到一个文件中。
{% note default modern %}
因为使用了命名空间,我们可以安心的使用简单的变量命名例如 $width
, $height
。这与使用 @import
不同,它避免了使用像 $foo-bar-width
这样繁琐的命名来防止名称冲突。且能让你的代码更加简洁易懂。
{% endnote %}
{% tabs %}
// src/_corners.scss
$radius: 3px;
@mixin rounded {
border-radius: $radius;
}
// style.scss
@use 'src/corners';
.button {
@include corners.rounded;
padding: 5px + corners.$radius;
}
.button {
border-radius: 3px;
padding: 8px;
}
{% endtabs %}
默认情况下,命名空间是 URL 最后一个部分,且不带扩展名。然而你可能想要选择一个不同的命名空间,比如更短的命名空间,又或者需要加载多个文件名相同的模块。此时你可以通过 @use "<url>" as <namespace>
来自定义命名空间。
{% tabs %}
// src/_corners.scss
$radius: 3px;
@mixin rounded {
border-radius: $radius;
}
// style.scss
@use 'src/corners' as c;
.button {
@include c.rounded;
padding: 5px + c.$radius;
}
.button {
border-radius: 3px;
padding: 8px;
}
{% endtabs %}
甚至还可以通过 @use "<url>" as *
来忽略命名空间。不过我们只建议在编写个人使用的样式表时这样做;否则他人使用时可能会发生命名冲突!
{% tabs %}
// src/_corners.scss
$radius: 3px;
@mixin rounded {
border-radius: $radius;
}
// style.scss
@use 'src/corners' as *;
.button {
@include rounded;
padding: 5px + $radius;
}
.button {
border-radius: 3px;
padding: 8px;
}
{% endtabs %}
作为一个样式表作者,你可能希望用户在样式表外部只能访问你让他们访问的成员。Sass
提供了一种简单的方式来定义私有成员,成员名称以 -
或者 _
都视为私有成员。在你的样式表内部可以正常访问这些成员,但这些成员不会存在于向外部公开的模块接口中。这意味着加载你模块的样式表看不见他们。
{% note default modern %}
如果你想让模块对整个包而不是单个模块不可见,你只需要避免在你包的任意入口(你告知用户的包入口)转发(forward
)模块即可。当然也可以在转发模块时隐藏部分成员
{% endnote %}
// src/_corners.scss
$-radius: 3px;
@mixin rounded {
border-radius: $-radius;
}
// style.scss
@use 'src/corners';
.button {
@include corners.rounded;
// 这里会报错 $-radius 在 `_corners.scss` 外部是不可访问的.
padding: 5px + corners.$-radius;
}
定义变量时,通过 !default
这个 flag 使其变为可配置的。使用 @use <url> with ( <variable>: <value>, <variable>: <value> )
在加载模块时应用配置。配置中的值会覆盖模块中对应变量的默认值。
{% tabs %}
// _library.scss
$black: #000 !default;
$border-radius: 0.25rem !default;
$box-shadow: 0 0.5rem 1rem rgba($black, 0.15) !default;
code {
border-radius: $border-radius;
box-shadow: $box-shadow;
}
// style.scss
@use 'library' with (
$black: #222,
$border-radius: 0.1rem
);
code {
border-radius: 0.1rem;
box-shadow: 0 0.5rem 1rem rgba(34, 34, 34, 0.15);
}
{% endtabs %}
使用 @use ... with
配置模块非常方便,尤其是在使用一开始用 @import
编写的库时。但它并不是特别灵活,我们不建议将其用于更高级的用例。如果您发现自己想将 Map
作为配置传递从而一次配置多个变量,或者在模块加载后更新配置,请考虑编写一个 mixin
来设置您的变量,然后再编写一个 mixin
来注入您的样式。
{% tabs %}
// _library.scss
$-black: #000;
$-border-radius: 0.25rem;
$-box-shadow: null;
/// 如果用户配置了 `$-box-shadow`,使用用户配置的值。
/// 否则返回使用 `$-black` 组合成的默认值。
@function -box-shadow() {
@return $-box-shadow or (0 0.5rem 1rem rgba($-black, 0.15));
}
// 对外暴露用于配置的 mixin
@mixin configure($black: null, $border-radius: null, $box-shadow: null) {
@if $black {
$-black: $black !global;
}
@if $border-radius {
$-border-radius: $border-radius !global;
}
@if $box-shadow {
$-box-shadow: $box-shadow !global;
}
}
// 对外暴露用于注入样式的 mixin
@mixin styles {
code {
border-radius: $-border-radius;
box-shadow: -box-shadow();
}
}
// style.scss
@use 'library';
// 配置已经载入的模块 library
@include library.configure($black: #222, $border-radius: 0.1rem);
// 使用配置后的样式
@include library.styles;
code {
border-radius: 0.1rem;
box-shadow: 0 0.5rem 1rem rgba(34, 34, 34, 0.15);
}
{% endtabs %}
加载一个模块后,你可以重新定义其变量。
// _library.scss
$color: red;
// _override.scss
@use 'library';
library.$color: blue;
// style.scss
@use 'library';
@use 'override';
@debug library.$color; //=> blue
在使用 as *
时这种方法依然有效
// _library.scss
$color: red;
// _override.scss
@use 'library' as *;
$color: blue;
// style.scss
@use 'library';
@use 'override';
@debug library.$color; //=> blue
{% note default modern %}
@debug
可以在控制台(不是浏览器的)打印值
{% endnote %}
你可以在引入时省略扩展名 @use "variables"
,Sass
会自动加载 variables.scss
,variables.sass
或 variables.css
。
{% note warning modern %}
为了确保跨平台兼容性,所有路径均使用正斜杠 /
{% endnote %}
用户可以提供加载路径给 Sass
解析,Sass
在定位模块时将使用加载路径。例如 node_modules/susy/sass
作为一个加载路径,你可以通过 @use "susy"
来加载 node_modules/susy/sass/susy.scss
。
Sass
会优先加载当前文件目录下的模块,如果不存在才会使用加载路径。这可以确保在添加新库时不会出现错误的引入。
{% note default modern %}
Sass
的路径总是相对的,./
并不是必须的
{% endnote %}
作为一个约定,以 _
开头的 Sass
文件被认作为一个模块。这种文件被称为“块”,Sass
会避免单独编译这些文件。在引入块时可以忽略 _
。同时
如果在某个文件夹中有一个 _index.scss
或 _index.sass
文件,在以该文件夹为路径加载时,该主文件会被自动载入。
{% tabs %}
// foundation/_code.scss
code {
padding: 0.25em;
line-height: 0;
}
// foundation/_lists.scss
ul,
ol {
text-align: left;
& & {
padding: {
bottom: 0;
left: 0;
}
}
}
// foundation/_index.scss
@use 'code';
@use 'lists';
// style.scss
// 自动引入了 foundation/_index.scss
@use 'foundation';
code {
padding: 0.25em;
line-height: 0;
}
ul,
ol {
text-align: left;
}
ul ul,
ol ol {
padding-bottom: 0;
padding-left: 0;
}
{% endtabs %}