百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

Sass 模块化革命:彻底掌握 @use 和 @forward 的进阶技巧

myzbx 2025-03-25 15:41 39 浏览

前言

在上一篇中,我们深入探讨了 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 引入同一个模块时,需要注意以下两点:

  1. 模块加载机制:@forward 和 @use 引入同一个模块时不会重复导入。
  2. 推荐顺序:@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 代码更加模块化、可扩展且易于维护,助力高效开发!

相关推荐

零基础入门AI智能体:详细了解什么是变量类型、JSON结构、Markdown格式

当品牌跳出固有框架,以跨界联动、场景创新叩击年轻群体的兴趣点,一场关于如何在迭代中保持鲜活的探索正在展开,既藏着破圈的巧思,也映照着与新一代对话的密码。在创建AI智能体时,我们会调用插件或大模型,而在...

C# 13模式匹配:递归模式与属性模式在真实代码中的性能影响分析

C#13对模式匹配的增强让复杂数据处理代码更简洁,但递归模式与属性模式的性能差异一直是开发者关注的焦点。在实际项目中,选择合适的模式不仅影响代码可读性,还可能导致执行效率的显著差异。本文结合真实测试...

零基础快速入门 VBA 系列 6 —— 常用对象(工作簿、工作表和区域)

上一节,我介绍了VBA内置函数以及如何自动打字和自动保存文件。这一节,我们来了解一下Excel常用对象。Excel常用对象Excel有很多对象,其中最常用也最重要的包括以下3个:1.Workbo...

不同生命数字的生肖龙!准到雷普!

属龙的人总在自信爆棚和自讨苦吃之间反复横跳?看完这届龙宝宝的日常我悟了。属龙的人好像天生自带矛盾体:领导力超强可人缘时好时坏,工作雷厉风行却总在爱情里翻车。关键年份的龙性格差异更大——76年龙靠谱但不...

仓颉编程语言基础-面向对象编程-属性(Properties)

属性是仓颉颉中一种强大的机制,它允许你封装对类(或接口interface、结构体struct、枚举enum、扩展extend)内部状态的访问。它看起来像一个普通的成员变量(字段),但在其背后,它通过...

Python中class对象/属性/方法/继承/多态/魔法方法详解

一、基础入门:认识类和对象1.类和对象的概念在Python中,类(class)是一种抽象的概念,用于定义对象的属性和行为,而对象(也称为实例)则是类的具体表现。比如,“汽车”可以是一个类,它有...

VBA基础入门:搞清楚对象、属性和方法就成功了一半

如果你刚接触VBA(VisualBasicforApplications),可能会被“对象”“属性”“方法”这些术语搞得一头雾水。但事实上,这三个概念是VBA编程的基石。只要理解它们之间的关系,...

P.O类型文推荐|年度编推合集(一百九十五篇)

点击左上方关注获取更多精彩推文目录2019年度编推35篇(1V1)《悖论》作者:流苏.txt(1V1)《桂花蒸》作者:大姑娘浪.txt(1V1)《豪门浪女》作者:奚行.txt...

Python参数传递内存大揭秘:可变对象 vs 不可变对象

90%的Python程序员不知道,函数参数传递中可变对象的修改竟会导致意想不到的副作用!一、参数传递的本质:对象引用传递在Python中,所有参数传递都是对象引用的传递。这意味着函数调用时传递的不是对...

JS 开发者必看!TC39 2025 最新动向,这些新语法要火?

大家好,很高兴又见面了,我是"高级前端进阶",由我带着大家一起关注前端前沿、深入前端底层技术,大家一起进步,也欢迎大家关注、点赞、收藏、转发,您的支持是我不断创作的动力。TC39第...

2025 年值得尝试的 5 个被低估的 JavaScript 库

这些JavaScript库可能不会在社交媒体或HackerNews上流行起来,但它们会显著提高您的工作效率和代码质量。JavaScript不再只是框架。虽然React、Vue和Sv...

Python自动化办公应用学习笔记30—函数的参数

一、函数的参数1.形参:o定义:在函数定义时,声明在函数名后面括号中的变量。o作用:它们是函数内部的占位符变量,用于接收函数被调用时传入的实际值。o生命周期:在函数被调用时创建,在函数执...

16种MBTI人格全解析|测完我沉默了三秒:原来我是这样的人?

MBTI性格测试火了这么久,你还不知道自己是哪一型?有人拿它当社交话题,有人拿它分析老板性格,还有人干脆当成择偶参考表。不废话,今天我一次性给你整理全部16种MBTI人格类型!看完你不仅能知道自己是谁...

JS基础与高级应用: 性能优化

在现代Web开发中,性能优化已成为前端工程师必须掌握的核心技能之一。本文从URL输入到页面加载完成的全过程出发,深入分析了HTTP协议的演进、域名解析、代码层面性能优化以及编译与渲染的最佳实践。通过节...

爱思创CSP-J/S初赛模拟赛线上开赛!助力冲入2024年CSP-J/S复赛!

CSP-J/S组初赛模拟赛爱思创,专注信奥教育19年,2022年CSP-J/S组赛事指定考点,特邀NOIP教练,开启全真实CSP-J/S组线上初赛模拟大赛!一、比赛对象:2024年备考CSP-J/S初...