Sass 的 @if 仅在编译时生效,无法实现运行时主题切换;它根据预设常量生成固定 CSS,而动态切换需靠 CSS 自定义属性配合 JS 修改 document.documentElement 或 class。

Sass 的 @if 无法实现“动态”主题切换——它只在编译时起作用,生成的是固定 CSS,和用户点击、系统偏好、JS 控制完全无关。 真正能响应式切换的,必须靠 CSS 自定义属性(--theme-color)配合 JS 修改 document.documentElement 或 class;@if 只负责“哪套样式该进最终文件里”。
为什么 @if $theme == 'dark' 不会随用户操作变色
常见错误现象:@if 块里的样式没生效,或切主题后页面毫无反应。这是因为:
-
@if在你运行sass命令或启动构建工具(如 Webpack/Vite)时就执行完了,此时浏览器还没打开,document根本不存在 - 变量
$theme必须是 Sass 编译期已知的常量(如$theme: dark;),不能来自localStorage、prefers-color-scheme或 JS 动态赋值 - 编译结果里不会保留
@if结构,只有它“选中”的那部分 CSS 被写入输出文件,其余被直接丢弃 - 如果你用
vue-cli-plugin-style-resources-loader注入变量,它也只是把变量提前塞进编译上下文,仍不等于运行时可变
@if 真正适合的场景:构建时多版本打包
当你需要为不同客户、环境或部署目标生成差异化的 CSS 文件时,@if 才发挥价值。例如:
- 内部系统用深蓝主色,对外交付版用绿色:
@if $brand == "internal" { .header { background: #0052cc; } } - 是否启用过渡动画:
@if $enable-transitions { transition: all 0.2s ease; } - 根据构建参数决定单位:
@if $use-rem { font-size: 1rem; } @else { font-size: 16px; } - 兼容性兜底:为 IE11 单独加
filter,但只在$legacy-support为true时编译进去
关键操作:用 sass --define=brand=internal main.scss:css/internal.css 和 --define=brand=customer 分别跑两次编译。
立即学习“前端免费学习笔记(深入)”;
别混淆 @if 和 if() 函数
这是最常踩的坑:把指令当函数用,或反过来。
-
@if $dark-mode { color: white; }是指令,控制代码块是否输出到 CSS —— 它不返回值,也不能嵌在属性里 -
color: if(lightness($primary) > 150, #1e293b, #f1f5f9);是函数,返回一个颜色值,可赋给变量或直接用于属性 -
if()只能用 Sass 内置函数(如lightness()、type-of())作条件,不能读取媒体查询或 DOM 状态 - 想在单个属性里做响应式判断?那是 CSS 原生的
if(media(width > 800px) blue, else red),和 Sass 无关
真正可切换的主题,必须分两层实现
第一层(Sass 负责):生成含自定义属性的通用 CSS,所有颜色/尺寸都基于 var(--primary-color) 等声明;第二层(JS 负责):运行时修改这些变量值或切换 data-theme 类名。
- SCSS 中写:
$primary: var(--primary-color, #2196F3); .btn { background: $primary; } - JS 中切主题:
document.documentElement.style.setProperty('--primary-color', '#177ddc');或document.documentElement.setAttribute('data-theme', 'dark'); - 千万别在
@if里尝试读取getComputedStyle(document.body).getPropertyValue('--theme')——它永远是空字符串,因为此时 CSS 还没加载完 - 如果用 UniApp/Vue,Vuex/Pinia 存主题名,再通过
:style="{ '--primary-color': themeVars.primary }"绑定,比全局setProperty更可控
最容易被忽略的一点:Sass 编译出的 CSS 里,var(--x) 是原样保留的字符串,不是值;它的解析发生在浏览器渲染阶段,和 @if 毫无关系——这正是动态切换能成立的前提。


















