必须在 main.ts 顶部第一行导入 vars.css,否则 CSS 变量无法在首次渲染前挂载到 :root,导致组件中 var(--xxx) 失效;且 vars.css 内只能声明 :root 或原子类,不可含带权重的选择器或全局规则,避免被 scoped 样式压制或覆盖。

必须在 main.ts(或 main.js)顶部第一行导入,且该文件只能声明 :root 或原子类,不能含带权重的选择器——否则会被 scoped 组件样式压制。
为什么 main.ts 导入后组件里还是用不到 var(--xxx)
不是导入位置错了,就是文件内容越界了。常见错误包括:
- 把
vars.css放在@/styles/下,但main.ts里写的是import './styles/vars.css'(路径错,别名未生效) -
vars.css里写了body { margin: 0; }这类全局规则——它会被打包进 CSS,但不会注入到 :root,且可能被后续样式覆盖 - 在
App.vue的<style scoped>里写color: --primary-color(漏了var(),浏览器直接忽略整条声明) - 用了
scoped却想让.btn类生效,但没把.btn抽到独立的base.css并在main.ts中显式导入
vite.config.ts 里配 additionalData 对 CSS 变量无效
这是最常被混淆的一点:css.preprocessorOptions.scss.additionalData 只影响 SCSS 编译期,对纯 CSS 文件里的 :root 声明完全不生效。它解决的是 $primary-color 这类 SCSS 变量,不是 --primary-color 这类 CSS 自定义属性。
如果你要同时支持两种变量:
立即学习“前端免费学习笔记(深入)”;
- CSS 变量 → 必须靠
main.ts导入vars.css - SCSS 变量 → 配
additionalData: '@import "@/styles/variables.scss";',且确保variables.scss只含$开头变量、@mixin、@function - 两者路径别名都得靠
resolve.alias显式配置,Vite 不会自动把@透传给 CSS 预处理器
Scoped 组件里怎么安全复用全局类名
写了 <style scoped> 后,.text-lg 这种类名不会自动带上 data-v-xxx 属性,所以全局定义的 .text-lg 根本不匹配。
有三个务实选择:
- 把真正需要复用的原子类(如
.flex、.text-center、.p-4)全抽到一个base.css,并在main.ts里import '@/styles/base.css' - 在组件内用
::v-deep(.text-lg) { ... }穿透(仅限局部覆盖,别滥用) - 彻底放弃 scoped,改用 CSS Modules 或基于 BEM 的命名约束(适合中大型项目,但需团队共识)
最容易被忽略的其实是顺序:CSS 变量必须在任何组件渲染前就挂载到 document 上。哪怕只晚一行 import,var(--xxx) 在首次渲染时就会 fallback 到初始值或继承值,且不会响应式更新——这不是 bug,是 CSS 自定义属性的加载机制决定的。


















