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

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

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

前言

在上一篇中,我们深入探讨了 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 代码更加模块化、可扩展且易于维护,助力高效开发!

相关推荐

别再问Cookie了,再问就崩溃了!_别问 再问

作者:懿来自:Java极客技术说实话,之前面试都是直接去背诵的面试题,关于Cookie的一些内容,比如说,记录浏览器端的数据信息啦,Cookie的生命周期啦,这些内容,也从来没有研究过C...

5分钟学会物流轨迹地图API嵌入到页面中,实现物流轨迹可视化

前言在电子商务和在线购物日益普及的今天,为用户提供实时的物流信息已成为提升客户满意度的关键。本文将指导您如何在网页中嵌入物流轨迹地图API,以便用户能够直观地跟踪他们的包裹。1.申请接口、获取API密...

Springboot项目中几种跨域的解决方法

环境:springboot2.3.9.RELEASE什么是跨源资源共享跨源资源共享(CORS)(或通俗地译为跨域资源共享)是一种基于HTTP头的机制,该机制通过允许服务器标示除了它自己以外的其它...

基于Java实现,支持在线发布API接口读取数据库,有哪些工具?

基于java实现,不需要编辑就能发布api接口的,有哪些工具、平台?还能一键发布、快速授权和开放提供给第三方请求调用接口的解决方案。架构方案设计:以下是一些基于Java实现的无需编辑或只需少量编辑...

Axios VS Fetch, 用哪个更好?详细对比附案例

在JavaScript中进行HTTP请求时,最常用的两个工具是:原生fetchAPI流行的第三方库Axios我都在生产环境中使用过这两个工具。虽然两者都表现良好,但有时我会后悔选择了其中一个而非另一...

Ollama:Web搜索API和MCP_oalib search

如果您曾经尝试过LLM,您就会明白其中的痛点:模型在模式匹配方面非常出色,但往往会虚构一些东西。如果你问起上周发生的事情,突然间,您得到的只是来自2022年的鬼故事。这次更新改变了这一切。基本上...

基于浏览器扩展 API Mock 工具开发探索|得物技术

一、前言在日常开发过程中,偶尔会遇到后端接口未完成或者某个环境出现问题需要根据接口返回来复现等等场景。刚好最近在学习浏览器插件的相关知识,并在此背景下开发了一款基于浏览器插件的Mock工具。该工...

JavaScript动态注入的几种方法_js动态引入js

在现代的Web开发中,JavaScript动态注入是一个强大的技术,它允许开发者在网页运行时动态地修改网页内容和行为,方便进行调试和维护。动态注入通常涉及以下几个关键概念:DOM(文档对象模型)、和...

面试官:如何通过 MyBatis 查询千万数据并保证内存不溢出?

推荐学习真香警告!Alibaba珍藏版mybatis手写文档,刷起来牛掰!“基础-中级-高级”Java程序员面试集结,看完献出我的膝盖闭关28天,奉上[Java一线大厂高岗面试题解析合集],备战金九银...

nextjs教程三:获取数据_nextcloud数据迁移

数据的获取数据获取是任何应用程序中最重要的部分,本文将介绍,如何在react,nextjs中获取数据主要有种方法可以获取数据在服务端,用fetch获取数据在客户端,通过路由处理器获取数据下面分别...

Fetch API 教程_fetch_all

JavaScript初学者学完语法,进入实际的网页编程,一定有人告诉你,要掌握一个叫做XMLHttpRequest的东西。脚本都靠它发出HTTP请求,跟服务器通信。所谓的AJAX操作就是...

Mozilla火狐39.0正式版增加Emoji支持

2015-07-0310:41:43作者:李熙Mozilla旗下浏览器火狐(Firefox)39.0正式版在今日发布,新版在性能上改进不大,着重于浏览器的功能和细节改进:新版提升了Firefox...

如何设计前端监控sdk,实现前端项目全链路监控

一、埋点系统设计与实现(文章最后有如何回答)1.埋点分类1.1手动埋点(代码埋点)//业务代码中主动调用tracker.track('button_click',{&nbs...

如何快速实现一套流程编排系统,前端开发组件都有哪些,一篇搞懂

早上9点,AI产品经理紧急拉会:“我们的客户明天要看到AI审批流程原型,传统开发至少要一周,有什么办法今天就能上线?”这时,你打开流程编排画布,拖拽几个节点,连接大模型API和服务,1小时后客户竖起...

2023金九银十必看前端面试题!2w字精品!

导文2023金九银十必看前端面试题!金九银十黄金期来了想要跳槽的小伙伴快来看啊CSS1.请解释CSS的盒模型是什么,并描述其组成部分。答案:CSS的盒模型是用于布局和定位元素的概念。它由内容区域...