用 Sass 模块化系统取代全局导入,消除 1.80.0 引入的 @import 弃用警告

目录

前言

问题

[@import 的缺陷](#@import 的缺陷)

命名冲突

重复导入

模块系统

[@use 规则](#@use 规则)

[@forward 规则](#@forward 规则)

实际修改


前言

最初,Sass 使用 @import 规则通过单个全局命名空间加载其他文件,所有内置函数也可全局使用。由于模块系统(@use@forward 规则)已经推出多年,我们现已弃用 Sass @import 规则和全局内置函数。

@import 会导致许多问题,需要手动为 Sass 成员指定命名空间以避免冲突,当多次导入同一个文件时会减慢编译速度,并且使人类和工具都很难分辨给定变量、混合或函数来自何处。

模块系统修复了这些问题,并使 Sass 的模块化与其他现代语言的最佳实践相媲美,但只要 @import 仍保留在语言中,我们就无法获得它的全部好处。

@import 现在从 Dart Sass 1.80.0 开始已弃用。此外,我们还将弃用 sass: 模块中可用的 Sass 内置函数的全局版本。

问题

将 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";

@forward 规则用于将一个模块的内容"转发"到另一个模块中。这样,你可以在不直接使用模块的情况下,通过转发的模块间接使用原始模块的内容。

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

复制代码
px sass-migrator module --verbose --migrate-deps styles.scss

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

实际修改

1. Sass @import rules are deprecated and will be removed in Dart Sass 3.0.0

sass 1.80 不再支持 @import 需要使用 `@use'

  • 错误提示:

    Deprecation Warning on line 1, column 9 of src\About.vue:
    Sass @import rules are deprecated and will be removed in Dart Sass 3.0.0.

    More info and automated migrator: https://sass-lang.com/d/import

    1 │ @import "@/variables.scss";
    │ ^^^^^^^^^^^^^^^^^^

  • 修改方法:

    @use "@/variables.scss";

Deprecation Warning: The legacy JS API is deprecated and will be removed in Dart Sass 2.0.0.

sass 1.80 不再支持老的 js api 接口

  • 错误提示:

    Deprecation Warning: The legacy JS API is deprecated and will be removed in Dart Sass 2.0.0.

    More info: https://sass-lang.com/d/legacy-js-api

  • 修改方法 vue.config.js:

    export default defineConfig({
    ...
    css: {
    preprocessorOptions: {
    scss: {
    api: 'modern-compiler', // 修改api调用方式
    },
    },
    },
    ...
    });

Internal server error: [sass] Undefined variable.

sass 1.80 全局变量和 mixin 需要手动导出

  • 错误提示:

    14:20:18 [vite] Internal server error: [sass] Undefined variable.

    13 │ color: $color-blue;
    │ ^^^^^^^^^^^

    14:18:52 [vite] Internal server error: [sass] Undefined mixin.

    6 │ @include func(css);
    │ ^^^^^^^^^^^^^^^

  • 修改方法 vue.config.js:

    export default defineConfig({
    ...
    css: {
    preprocessorOptions: {
    scss: {
    api: "modern-compiler",
    javascriptEnabled: true, // 启用javascript
    additionalData: '@use "@/style/variable.scss" as *;',
    },
    },
    },
    ...
    });

相关推荐
江城开朗的豌豆1 分钟前
JavaScript篇:对象派 vs 过程派:编程江湖的两种武功心法
前端·javascript·面试
不吃糖葫芦32 分钟前
App使用webview套壳引入h5(二)—— app内访问h5,顶部被手机顶部菜单遮挡问题,保留顶部安全距离
前端·webview
江城开朗的豌豆23 分钟前
JavaScript篇:字母侦探:如何快速统计字符串里谁才是'主角'?
前端·javascript·面试
kite01217 小时前
浏览器工作原理06 [#]渲染流程(下):HTML、CSS和JavaScript是如何变成页面的
javascript·css·html
coding随想9 小时前
JavaScript ES6 解构:优雅提取数据的艺术
前端·javascript·es6
小小小小宇9 小时前
一个小小的柯里化函数
前端
灵感__idea9 小时前
JavaScript高级程序设计(第5版):无处不在的集合
前端·javascript·程序员
小小小小宇9 小时前
前端双Token机制无感刷新
前端
小小小小宇9 小时前
重提React闭包陷阱
前端
小小小小宇9 小时前
前端XSS和CSRF以及CSP
前端