SCSS中以下划线开头的变量并非自动私有,仅是社区约定;真正实现私有封装需依赖@use规则创建模块作用域,此时$_变量默认不导出,须显式@forward才能暴露。

SCSS 中以下划线开头的变量是私有变量吗?
不是自动私有的,下划线 _ 本身不触发作用域隔离,它只是 SCSS 社区约定的「私有标识符」,编译器完全不检查、不保护。你 @import 一个带 $_color-primary 的文件,变量照样能被外部读取和覆盖。
真正起作用的是 @use 规则 —— 它才是模块化和变量封装的基础设施。只有用 @use 加载的模块,其以 $_ 开头的变量才默认不可被外部访问。
-
@import是全局合并,所有变量、mixin、function 全部平铺到全局作用域,$_纯属“自我提醒” -
@use创建独立模块作用域,$_变量默认不导出,必须显式用@forward ... with或!default覆盖才能暴露 - 混合使用
@import和@use会导致行为不一致,建议全项目统一迁移到@use
用 @use 实现真正的私有变量封装
假设你有一个颜色配置文件 _colors.scss:
// _colors.scss $color-primary: #007bff !default; $_color-warning: #ffc107; // 私有,不导出 $color-success: #28a745 !default;
在另一个文件中 @use 它:
立即学习“前端免费学习笔记(深入)”;
// buttons.scss
@use 'colors';
.button {
color: colors.$color-primary; // ✅ 可访问(无下划线,自动导出)
background: colors.$color-success; // ✅ 可访问
border-color: colors.$_color-warning; // ❌ 编译报错:Undefined variable.
}
- SCSS 默认只导出不以下划线开头的变量(以及 mixin/function)
- 想让某个
$_变量“破例导出”,得用@forward 'colors' with ($_color-warning: #ffc107),但一般不推荐 - 如果变量本身带
!default,且你在@use时传入同名变量,它会被覆盖 —— 这是配置注入的常用方式
为什么 $_ 变量仍可能“泄漏”?常见踩坑点
即使用了 @use,私有性也可能被意外破坏:
- 在同一个文件里多次
@use同一模块(如@use 'colors' as c1和@use 'colors' as c2),它们之间不共享私有变量,但若某处误用@import,就全崩了 -
@forward时没加as *或with控制,会把源模块所有可导出项原样转发,包括你不希望暴露的变量 - 用
@use 'colors' as *会把所有可导出变量挂到当前作用域,此时$color-primary可用,但$_color-warning依然不可用 —— 注意这不是“提升为全局”,只是解构导入 - VS Code 或 Webstorm 的 SCSS 插件有时会错误提示
$_变量“未定义”,其实只是编辑器没正确解析@use作用域,不影响编译
实际项目中怎么组织模块与私有变量?
典型结构如下(基于 Dart Sass 1.23+):
// scss/config/_index.scss @use 'breakpoints'; @use 'z-index'; @use 'typography'; // 导出公共配置,隐藏内部计算逻辑 @forward 'breakpoints' with ( $_max-width: 1200px !default ); @forward 'z-index'; @forward 'typography';
其中 _breakpoints.scss 内部可能有:
// _breakpoints.scss $_max-width: 1440px !default; $breakpoint-sm: 576px; $breakpoint-md: 768px; $breakpoint-lg: $_max-width; // 用私有变量驱动公共变量
- 把计算逻辑、中间值、主题开关等放在
$_变量里,只暴露最终结果给使用者 - 每个模块保持单一职责,
@forward时控制粒度,避免“全量转发” - 私有变量名不必过度缩写,比如
$_grid-gutter-default比$_gtr更利于维护
模块化不是靠命名规范撑起来的,而是靠 @use + @forward + 下划线约定三层配合。少一层,私有性就少一分保障。


















