scss变量($xxx)仅在编译期存在,会被直接替换为固定值,不会出现在最终css中,因此无法响应js动态修改或系统偏好变化;css变量(--xxx)则在运行时生效,支持继承、作用域覆盖和js操作。

SCSS变量($xxx)根本不会出现在浏览器里
编译后所有 $color-primary 都被替换成 #3b82f6 这类固定值,CSS 文件里搜不到任何 $ 符号。这意味着:你改了 JS 里的 document.documentElement.style.setProperty('--color-primary', 'red'),对它完全没影响——它压根不认这个变量。
常见错误现象:
- 写了
$primary: var(--primary),以为能桥接运行时,结果编译报错或输出var(--primary)字面量(因为 SCSS 不解析 CSS 函数) - 在
@media (prefers-color-scheme: dark)里用$bg切背景色,发现深色模式下还是白底——$bg在编译时就定死了
CSS变量(--xxx)必须靠选择器作用域控制覆盖逻辑
--gap 在 :root 定义是全局默认值,但写成 .card { --gap: 24px; } 后,所有 .card 内部的 var(--gap) 就自动取 24px,子元素继承、局部重定义、JS 动态修改全都能响应。
使用场景与要点:
- 主题切换必须用
--el-color-primary这类原生变量,Element Plus 的暗色主题就是靠它驱动的 - 不要试图用
#{$name}拼出var(--#{$name})——#{}只在编译期生效,而var()是运行时语法,两者根本不在一个时间维度上 - 若依 Vue3 项目里改 Element Plus 主色,直接覆盖
:root下的--el-color-primary就行,比重编译 SCSS 快 10 倍且无需刷新
插值 #{} 不是“动态”,只是字符串缝合
.btn-#{$size} 看似灵活,其实只是编译器把 $size 的当前值(比如 "lg")硬塞进字符串,产出 .btn-lg。它不监听变量变化,不触发重渲染,也不和 Vue 的响应式挂钩。
容易踩的坑:
-
.icon-$type漏写#{}→ 输出无效选择器.icon-$type,浏览器静默忽略 -
.foo-#{$size} -bar中多了一个空格 → 编译成.foo-md -bar(两个独立选择器),而非预期的.foo-md-bar - 用
#{}拼--#{$name}后,想在别处var(--#{$name})引用——不行,后者里的#{$name}不会被 SCSS 处理,浏览器直接当非法自定义属性丢弃
什么时候该用哪个?看更新时机
如果颜色/尺寸在项目上线后**永远不会变**(比如设计系统基础间距体系),用 $spacing-xs 没问题;一旦涉及用户点击切换、系统偏好联动、权限驱动样式差异,就必须交给 --spacing-xs。
真实项目中的分界点:
- 若依后台要加“品牌色配置页”,让用户输入 HEX 值实时预览——只能走 CSS 变量 + JS 注入,SCSS 变量方案在此刻彻底失效
- Vite 插件
vite-plugin-theme能提取并注入 CSS 变量,但它对$变量无能为力,因为构建阶段早已结束 - SCSS 的
!default适合做库级默认配置(如_variables.scss中$border-radius: 4px !default),但无法替代运行时的--border-radius覆盖能力
$ 变量计算),再用 CSS 变量做运行时微调。这时得明确哪些值归编译管、哪些归 DOM 管,否则两套机制互相覆盖,调试时连 console 都看不出是哪一层生效的。前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











