前言 

Sass 2.0 即将发布,更多的功能逐渐被弃用并产生警告。我在上一篇文章中介绍了部分不兼容的变化,此文是对该文章的补充。从 1.80.0 版本开始 @import 被弃用,本文将解释其原因、入门模块化系统(替代方案)并提供警告的消除方法。

如果你想快速解决问题,请优先看解决章节。

问题

将 Sass 升级到 1.80.0 及以上版本后,你可能会遇到如下警告:

WARN  Dart Sass: [file].scss:[line]:8: Sass @import rules are deprecated and will be removed in Dart Sass 3.0.0.

该警告表示 @import 规则已被弃用,并且将在 Sass 3.0.0 版本中移除。这看上去很宽容,毕竟 Sass 2.0 还未发布呢。但实际上取代 @import 的现代化方案已经推出好几年了,可它仍然被广泛使用。这大概就是 Sass 在 2.0 之前就弃用它的原因吧。

这个警告是值得修正的,消除它的正面效果绝不仅是清理碍眼的输出。

@import 的缺陷 

命名冲突 

假设我们有以下三个 SCSS 文件:

// file1.scss
$color: blue;
@mixin button {
  // button styles
}
 
// file2.scss
$color: red; // 会覆盖 file1 的 $color
@mixin button {
  // 会覆盖 file1 的 button
  // different button styles
}
 
// main.scss
@import "file1";
@import "file2";
// 此时 $color 是 red, button mixin 是 file2 的版本

如上代码中的注释所示,@import 导致了命名冲突。因为 @import 是全局性的,而不是模块化的。对于 IDE 和工具而言也不友好,它们可能很难推断你使用的变量、mixin 等来自哪个文件。全局导入这种东西在现代化语言中不应该存在,或者限制使用。其副作用难以琢磨的。

重复导入 

再假设,你有如下文件:

// header.scss
@import "variables";
@import "mixins";
 
// footer.scss
@import "variables"; // 重复导入
@import "mixins"; // 重复导入
 
// main.scss
@import "header";
@import "footer";
// variables 和 mixins 被导入了两次

如上代码中的注释所示,@import 会重复导入。因为 @import 并不关心导入的文件是否已经导入过。重复导入会降低编译速度,在大型项目中尤为明显。

模块系统 

@use 规则 

基于 @import 的全局导入特性,我们很容易将变量集中到某个文件中然后直接引入到当前的代码环境中:

// app.scss
@import "vars/colors";
 
h1 {
  color: $title-color; // <- 直接使用 var/colors.scss 中的变量
}

将以上的 @import 改为 @use,现在 Sass 会为我们自动创建命名空间。以避免全局带来的冲突和污染:

// app.scss
@use "vars/colors";
 
h1 {
  color: colors.$title-color; // <- 使用命名空间访问 var/colors.scss 中的变量
}

注意,这会带来一个显著的迁移难题。首先,我们以及工具都可能很难推断此前代码中直接使用的变量来自哪里(换作模块化系统就是哪个命名空间),尤其是组织不严谨的项目。该如何准确修改呢?好办:

// app.scss
@use "vars/colors" as *;
 
h1 {
  color: $title-color; // <- 直接使用 var/colors.scss 中的变量
}

as 语法和 *,将所有成员直接导入进来。类似于 JS 的 import *。这样可以让我们的代码平缓迁移,逐渐的改用为命名空间。并且不存在全局范围带来的各种问题。

@forward 规则 

仅靠 @use 带来的模块化改变,有时候也会增加更多的样板代码。例如你有一批变量文件,按类型归类如 colors.scssfonts.scsssizes.scss 等。你需要在每一个使用变量的文件中分别导入它们。

组件 1:

// compoent1.scss
@use "vars/colors";
@use "vars/fonts";
@use "vars/sizes";
 
// compoent1 代码...

组件 2:

// compoent2.scss
@use "vars/colors";
@use "vars/fonts";
@use "vars/sizes";
 
// compoent2 代码...

使用 @forward 改善这一情况:

// vars.scss
@forward "colors";
@forward "fonts";
@forward "sizes";
 
// 更多的变量文件...

然后我们仅导入 vars 即可:

// compoent1.scss
@use "vars";
 
// component2.scss
@use "vars";

解决 

Sass 为我们提供了迁移工具 sass-migrator,通过它可以自动化的将样式代码迁移到模块化系统。运行命令:

px sass-migrator module --verbose --migrate-deps styles.scss

把 styles.scss 替换为你的入口样式文件即可。其中 --migrate-deps 参数表示不仅是修改显式传递给命令行的样式文件,还会修改它的依赖。基于模块系统章节的基本知识,你应该对修改会具有一些审查能力。

就本博客样式代码而言,它的主要修改如下:

// 修改前
@import "components/base.scss";
 
// 修改后
@use "sass:meta";
@include meta.load-css("components/base.scss");

上面的 meta.load-css 是一个内建 mixin,用以替代嵌套导入。请尽可能让自己了解迁移工具做的每一处修改,并确保新代码工作正常。对于更深入的知识,可以参考以下链接:

结束语 

这就是 Sass 的模块化系统的基础知识,以及如何消除 1.80.0 版本的 @import 弃用警告的方法了。本文的内容并不全面,旨在帮助一些人理解和消除最近的 Sass 版本中带来的警告。具体请参考链接中的更多资料。