解决 @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 中多次出现的冗余问题。

@use 必须放在除了 @forward 以外规则的上方,包括样式规则。但是在配置模块时可以在 @use 前定义变量。

1
2
3
4
5
// foundation/_code.scss
.code {
padding: 0.25em;
line-height: 0;
}
1
2
3
4
5
6
7
8
9
10
11
// foundation/_lists.scss
ul,
ol {
text-align: left;
& & {
padding: {
bottom: 0;
left: 0;
}
}
}
1
2
3
// style.scss
@use 'foundation/code';
@use 'foundation/lists';
1
2
3
4
5
6
7
8
9
10
11
12
13
14
code {
padding: 0.25em;
line-height: 0;
}

ul,
ol {
text-align: left;
}
ul ul,
ol ol {
padding-bottom: 0;
padding-left: 0;
}

加载成员

你可以通过 <namespace>.<name> 的形式来使用混入(mixin)函数(function)变量。在默认情况下,模块链接名就是 namespace 命名空间。

被加载的成员(变量、函数、混入)只能在加载它们的地方使用。其它样式表需要编写其自身的 @use 规则来使用他们。这有助于判断引用的成员来自哪里。如果你想一次性从多个文件中加载成员,你可以使用 @forward 将需要的成员全部转发到一个文件中。

因为使用了命名空间,我们可以安心的使用简单的变量命名例如 $width, $height。这与使用 @import 不同,它避免了使用像 $foo-bar-width 这样繁琐的命名来防止名称冲突。且能让你的代码更加简洁易懂。

1
2
3
4
5
6
// src/_corners.scss
$radius: 3px;

@mixin rounded {
border-radius: $radius;
}
1
2
3
4
5
6
7
// style.scss
@use 'src/corners';

.button {
@include corners.rounded;
padding: 5px + corners.$radius;
}
1
2
3
4
.button {
border-radius: 3px;
padding: 8px;
}

自定义命名空间

默认情况下,命名空间是 URL 最后一个部分,且不带扩展名。然而你可能想要选择一个不同的命名空间,比如更短的命名空间,又或者需要加载多个文件名相同的模块。此时你可以通过 @use "<url>" as <namespace> 来自定义命名空间。

1
2
3
4
5
6
// src/_corners.scss
$radius: 3px;

@mixin rounded {
border-radius: $radius;
}
1
2
3
4
5
6
7
// style.scss
@use 'src/corners' as c;

.button {
@include c.rounded;
padding: 5px + c.$radius;
}
1
2
3
4
.button {
border-radius: 3px;
padding: 8px;
}

甚至还可以通过 @use "<url>" as * 来忽略命名空间。不过我们只建议在编写个人使用的样式表时这样做;否则他人使用时可能会发生命名冲突!

1
2
3
4
5
6
// src/_corners.scss
$radius: 3px;

@mixin rounded {
border-radius: $radius;
}
1
2
3
4
5
6
7
// style.scss
@use 'src/corners' as *;

.button {
@include rounded;
padding: 5px + $radius;
}
1
2
3
4
.button {
border-radius: 3px;
padding: 8px;
}

私有成员

作为一个样式表作者,你可能希望用户在样式表外部只能访问你让他们访问的成员。Sass 提供了一种简单的方式来定义私有成员,成员名称以 - 或者 _ 都视为私有成员。在你的样式表内部可以正常访问这些成员,但这些成员不会存在于向外部公开的模块接口中。这意味着加载你模块的样式表看不见他们。

如果你想让模块对整个包而不是单个模块不可见,你只需要避免在你包的任意入口(你告知用户的包入口)转发(forward)模块即可。当然也可以在转发模块时隐藏部分成员

1
2
3
4
5
6
// src/_corners.scss
$-radius: 3px;

@mixin rounded {
border-radius: $-radius;
}
1
2
3
4
5
6
7
8
9
// 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> ) 在加载模块时应用配置。配置中的值会覆盖模块中对应变量的默认值。

1
2
3
4
5
6
7
8
9
// _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;
}
1
2
3
4
5
// style.scss
@use 'library' with (
$black: #222,
$border-radius: 0.1rem
);
1
2
3
4
code {
border-radius: 0.1rem;
box-shadow: 0 0.5rem 1rem rgba(34, 34, 34, 0.15);
}

使用 mixin

使用 @use ... with 配置模块非常方便,尤其是在使用一开始用 @import 编写的库时。但它并不是特别灵活,我们不建议将其用于更高级的用例。如果您发现自己想将 Map 作为配置传递从而一次配置多个变量,或者在模块加载后更新配置,请考虑编写一个 mixin 来设置您的变量,然后再编写一个 mixin 来注入您的样式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// _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();
}
}
1
2
3
4
5
6
7
8
// style.scss
@use 'library';

// 配置已经载入的模块 library
@include library.configure($black: #222, $border-radius: 0.1rem);

// 使用配置后的样式
@include library.styles;
1
2
3
4
code {
border-radius: 0.1rem;
box-shadow: 0 0.5rem 1rem rgba(34, 34, 34, 0.15);
}

重新定义变量

加载一个模块后,你可以重新定义其变量。

1
2
// _library.scss
$color: red;
1
2
3
// _override.scss
@use 'library';
library.$color: blue;
1
2
3
4
// style.scss
@use 'library';
@use 'override';
@debug library.$color; //=> blue

在使用 as * 时这种方法依然有效

1
2
// _library.scss
$color: red;
1
2
3
// _override.scss
@use 'library' as *;
$color: blue;
1
2
3
4
// style.scss
@use 'library';
@use 'override';
@debug library.$color; //=> blue

@debug 可以在控制台(不是浏览器的)打印值

模块加载

你可以在引入时省略扩展名 @use "variables"Sass 会自动加载 variables.scss,variables.sassvariables.css

为了确保跨平台兼容性,所有路径均使用正斜杠 /

加载路径

用户可以提供加载路径给 Sass 解析,Sass 在定位模块时将使用加载路径。例如 node_modules/susy/sass 作为一个加载路径,你可以通过 @use "susy" 来加载 node_modules/susy/sass/susy.scss

Sass 会优先加载当前文件目录下的模块,如果不存在才会使用加载路径。这可以确保在添加新库时不会出现错误的引入。

Sass 的路径总是相对的,./ 并不是必须的

作为一个约定,以 _ 开头的 Sass 文件被认作为一个模块。这种文件被称为“”,Sass 会避免单独编译这些文件。在引入块时可以忽略 _。同时

主文件

如果在某个文件夹中有一个 _index.scss_index.sass 文件,在以该文件夹为路径加载时,该主文件会被自动载入。

1
2
3
4
5
// foundation/_code.scss
code {
padding: 0.25em;
line-height: 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
// foundation/_lists.scss
ul,
ol {
text-align: left;

& & {
padding: {
bottom: 0;
left: 0;
}
}
}
1
2
3
// foundation/_index.scss
@use 'code';
@use 'lists';
1
2
3
// style.scss
// 自动引入了 foundation/_index.scss
@use 'foundation';
1
2
3
4
5
6
7
8
9
10
11
12
13
14
code {
padding: 0.25em;
line-height: 0;
}

ul,
ol {
text-align: left;
}
ul ul,
ol ol {
padding-bottom: 0;
padding-left: 0;
}