前言
在上一篇中,我们深入探讨了 Sass 中 @import 语法的局限性,正是因为这些问题,Sass 在 1.80 版本 后逐步弃用 @import,推出了更现代化的 @use 和 @forward 语法作为替代。在本文中,我们将深入解析 @use 和 @forward 的核心用法。
1. @use 用法
为了改进 @import 语法的局限性,Sass 团队引入了 @use 语法。作为一种更为现代和高效的模块化引入方式,@use 不仅解决了 @import 带来的诸多问题,还提供了更强的功能和灵活性。
1.1. 模块化与命名空间
@use 语法的一个核心优势在于它对模块化的支持。当你通过 @use 引入一个 SCSS 文件时,其中的变量、函数和混合宏并不会直接暴露在全局作用域中,而是被封装在一个特定的命名空间内。这种方式要求你通过明确的命名空间来访问这些内容,从而有效避免了全局作用域的污染,同时显著减少了命名冲突的风险。
这种命名空间可以理解为我们常用的模块化开发. 命名空间名称也可称为模块名称.
我们通过一个示例来理解模块化与命名空间
示例中, 我通过@debug 在编译过程中观察模块中变量的值。使用Sass 编译时控制台会输入如下结果
Debug: 命名空间: bar, Primary Color: green
在 SCSS 中,使用 @use 语法引入模块时,若直接指定文件路径(如 @use 'path/to/file';),SCSS 会将被导入模块的内容整体封装到一个模块中,并以文件的名称(不含扩展名和路径)作为模块的标识符。
在上面的示例中, main.scss使用@use直接导入了bar.scss 文件, 就会将bar.scss中所有的内容被合并到main.scss中, 并且使用文件名称bar作为模块名称.
封装模块后, 就可以使用模块的方式来访问bar.scss中的变量. 如bar.$color. 通过 bar.$color 访问其中的变量这种方式有效避免了全局作用域污染,同时强化了代码的模块化和可维护性。
1.2. @use 中 as 语法的使用
当使用文件名称作为默认模块标识时,可能会出现模块名冲突的问题,尤其是在项目中引入了不同路径下的同名文件时。为了解决这一问题,可以通过 as 关键字为模块指定一个自定义名称。
例如,@use 'path/to/button' as btn; 可以将 button 模块重命名为 btn,从而避免命名冲突,确保代码的清晰性和可维护性。
示例:
在这个示例中,main.scss 使用 @use 导入了 foo/bar.scss 文件 和bar.scss文件,此时两个文件默认都以文件名bar作为模块名. 编译时就会报错. 报错信息如下:
Error: There's already a module with namespace "bar".
此时就需要我们通过as语法创建自定义模块名称。如示例中@use "foo/bar.scss" as foo的导入方式. 会将foo/bar.scss模块重命名为foo。此时就需要通过foo.$color来访问模块中的变量.
上面@use 使用命名空间的语法在项目初始搭建使用没有任何问题, 但对于之前使用@import 语法的
1.3. as *语法的使用
如果你使用@use语法, 但并不想使用模块化. 就可以使用as *. 它表示将导入的模块的所有内容直接合并到当前文件中,并且不会创建一个命名空间。这样可以让导入的模块的所有变量、mixin、函数等直接在当前文件中使用,而不需要使用命名空间来访问它们。
示例:
在这个示例中,main.scss 使用 @use 导入了 bar.scss 文件,并通过 as * 将模块中的所有成员直接引入当前作用域,而无需创建命名空间。因此,bar.scss 中定义的变量、混合宏或函数可以直接在 main.scss 中使用,无需通过命名空间引用。这种写法能够简化代码,但可能会增加命名冲突的风险,因此需要谨慎使用。
1.4. 私有成员的访问
在 Sass 的 @use 语法中,私有化特性是核心机制之一。通过 @use 导入模块时,模块内部的私有变量、函数或混合宏默认是不可访问的,只有明确导出的内容才能被外部使用。这种方式极大地增强了代码的封装性和安全性,防止模块内部实现细节被外部访问或篡改,从而降低了潜在的错误风险。
在模块中,开发者可以通过为变量或函数命名时添加 $-* 或 $_* 前缀(例如 $-private-var 或 $_internal-function)来显式标记其为私有成员。这些私有成员仅在模块内部可见,无法通过 @use 在外部访问。
例如:
@use不会直接将文件中的私有变量暴露给外部,这意味着你可以更加清晰地控制哪些内容是公共的,哪些内容是私有的。这种严格的作用域控制机制,提升了代码的可维护性和安全性。
1.5. @use 中with默认值
在 Sass 的 @use 规则中,with 语句用于为模块中的变量提供默认值。通过 with,可以在引入模块时覆盖模块中定义的默认变量值,从而实现高度的灵活性和可配置性。
示例:
在 Sass 的 @use 语法中,默认值覆盖机制是一项非常实用的特性。它允许你在引入模块时为文件中的变量设置新的默认值,而无需修改原始文件。通过这种机制,你可以灵活地调整模块的行为或样式,特别适合开发可配置的 UI 组件或主题系统。
1.6. @use 导入问题
在 Sass 的 @use 语法中,模块的作用域是封闭的,这意味着每个模块只能访问自身显式导出的成员,而无法直接访问嵌套导入模块中的变量。例如,在以下导入链中:
main.scss -> bar.scss -> foo.scss
如果 main.scss 引入了 bar.scss,而 bar.scss 又引入了 foo.scss,main.scss 并不会自动获得访问 foo.scss 中变量的权限。这是为了确保模块的封装性,避免命名冲突和意外的依赖耦合。
示例:
此时编译就会报错, 在main.scss无论使用bar.@$color, 还是foo.$color都会报错
- 使用bar.$color报错提示: bar中没有定义$color变量
- 使用foo.$color报错提示: 在main中没有foo这个命名空间
为了解决这个问题,就需要用到 @forward 指令。
2. @forward 用法
2.1. 转发导入
@forward本质是转发模块资源,是用于组织各文件中模块资源的方法。将导入的模块转发导出.
例如:
在这个示例中:
- bar.scss 使用 @forward "foo" 将 foo.scss 的成员转发出去。
- main.scss 引入 bar 后,可以通过 bar 访问 foo 中的变量(如 $foo-color)。
- 整个过程并未直接加载或编译 foo.scss,而是通过 bar 将 foo 的成员暴露给外部使用。
@forward 的核心作用是将一个模块的成员转发到另一个模块中,而不会直接加载或编译这些文件。
但是在bar.scss中是不能直接访问foo.scss中的变量. 如果使用, 需要在bar.scss中使用@use
示例:
在bar.scss文件中
- @forward 的作用只是转发, 让导入bar.scss的文件可以通过bar.$xx访问foo中的变量
- @use 的作用是导入foo.scss, 并通过foo.$xx 访问foo 中的变量
在使用 Sass 的 @forward 和 @use 引入同一个模块时,需要注意以下两点:
- 模块加载机制:@forward 和 @use 引入同一个模块时不会重复导入。
- 推荐顺序:@forward 和 @use 先后顺序, 建议先写@forward 再 @use:
关于@forward 和 @use 先后顺序先后顺序, sass文档给出了描述, 大概的意思是这样的:
如果在同一个文件中同时使用 @forward 和 @use 引入同一个模块,推荐将 @forward 写在前面。这样做的好处是,如果使用者(可能是指其他开发者或文件)想要在引入模块时对转发的模块进行配置(例如通过 @use 的 with 语法),那么这些配置会优先应用到 @forward 转发的模块上,然后再由你的 @use 加载模块。这样可以确保模块的配置被正确处理,而不会因为顺序问题导致配置失效。
2.2. 添加前缀
为了更好地理解 @forward 添加前缀的作用,我们可以先思考一个问题:
如果一个文件中通过 @forward 转发了多个模块,而这些模块中存在同名变量会怎么样?
答案是:这会导致冲突并报错。因为外部文件在引入时,是通过当前文件模块(即转发文件)访问被转发模块中的变量。如果多个被转发模块中有同名的变量,Sass 无法区分这些变量到底属于哪个模块,从而导致命名冲突。
错误示例
编译时会报如下错误信息
Error: Two forwarded modules both define a variable named $color.
4 │ @forward "foo";
│ ━━━━━━━━━━━━━━ original @forward
5 │ @forward "baz";
│ ^^^^^^^^^^^^^^ new @forward
src\bar.scss 5:1 @use
src\main.scss 2:1 root stylesheet
此时就可以通过 @forward 的 as 关键字为转发的模块添加前缀,从而避免在引入模块时出现命名冲突。通过 as 前缀-* 的形式,你可以为转发的所有模块成员添加一个统一的前缀。当其他文件通过 @use 引入该模块时,成员名称会以 前缀- 开头,使得不同模块的同名成员能够清晰区分,增强了代码的可维护性和可读性。
使用前缀示例:
2.3. 控制可见性
大多数情况下,使用 @forward 转发模块时,并不需要转发模块的全部内容,只需要暴露外部文件通过 @use 引入时所需的部分即可。为了满足这一需求,Sass 提供了 hide 和 show 两种可见性控制机制,帮助我们灵活地管理模块成员的对外访问权限。
- hide:被 hide 的成员不会通过 @forward 转发,外部文件无法访问这些成员。它适用于隐藏模块中不需要公开的部分。
- show:只有被 show 的成员会被转发,其余成员对外不可见。它适用于明确指定模块中可以公开的部分。
通过 hide 和 show,我们可以精确控制模块的可见性,避免不必要的成员暴露,从而提升代码的封装性和安全性。
例如:
如果使用转发未公开的变量, 就会发生如下错误:
Error: Undefined variable.
8 │ width: bar.$width; // 报错: 转发隐藏
│ ^^^^^^^^^^
src\main.scss 8:10 root stylesheet
2.4. 转发时修改默认值
Sass 中,@forward 还可以与 with 结合使用,实现在转发模块时覆盖其默认变量(使用 !default 标记的变量)的值。这种方式非常适合在定制被转发模块的行为或样式时使用。
示例:
在 bar.scss 中,通过 @forward 转发 foo.scss 的变量时,可利用 with 修改其默认值。编译后,将优先使用转发时设置的新值,而非原始默认值。
如果你想要在使用 @use with 修改 @forward 转发的变量时,就需要在 @forward with 中为这些覆盖的变量添加 !default 标记,将覆盖的值也定义为默认值。这样一来,后续通过 @use with 修改时,就能轻松调整 @forward 中定义的变量值了。
示例:
需要注意的是在使用 @use with 时,不能添加 !default 字段,因为 @use with 的目标是直接覆盖模块中的变量值,而不是将它们标记为默认值。!default 仅适用于定义变量的初始值(如在模块内部或 @forward with 中)。
3. 总结
Sass 的 @use 和 @forward 语法是模块化开发的强大工具,彻底解决了传统 @import 的局限性。通过本文,我们深入探讨了:
- @use 的核心优势:包括命名空间、模块化、默认值覆盖、私有成员控制等功能,有效提升了代码的封装性和可维护性。
- @forward 的灵活应用:通过转发模块、添加前缀、控制可见性以及修改默认值等技巧,进一步优化了模块的组织与管理。
- 常见问题与解决方案:例如模块加载顺序、命名冲突、私有成员访问等问题,提供了清晰的操作指南和示例。
无论是构建可复用的 UI 组件,还是优化复杂的样式架构,@use 和 @forward 都能为你提供强大的支持。掌握这些进阶技巧,将使你的 Sass 代码更加模块化、可扩展且易于维护,助力高效开发!