CSS-in-JS 的核心思想是将 CSS 样式写在 JavaScript 代码中,利用编程语言的能力来生成和管理样式,从而解决传统 CSS 的全局污染、命名冲突、难以动态化等问题。在 React 生态中,styled-components 和 Emotion 等方案非常流行,但在 Vue 世界里,情况略有不同。
为什么 CSS-in-JS 在 Vue 中不是主流
Vue 单文件组件(SFC)原生提供了 scoped 样式隔离,配合 CSS Modules,已经能很好地解决样式冲突和局部作用域问题。大部分开发者认为,在组件内直接写 <style scoped> 是最自然、最高效的方式,不需要引入额外的运行时 CSS 生成开销。因此,专门为 Vue 设计的运行时 CSS-in-JS 库(如早期的 vue-styled-components)在社区中关注度较低,生态系统远不如 React 成熟。
但这并不意味着 Vue 完全排斥 CSS-in-JS 的理念。Vue 3.2 之后引入的 v-bind in CSS 特性,就是官方为“在样式中使用组件状态”提供的优雅方案,它既保留了 SFC 的开发体验,又实现了 CSS-in-JS 最核心的动态化价值。
官方方案:v-bind in CSS
在 Vue 3.2+ 的单文件组件中,你可以在 <style> 标签里通过 v-bind() 直接引用 <script setup> 中的响应式变量。Vue 会在底层将这些变量转换成 CSS 自定义属性(CSS Variables),并在值改变时自动更新对应样式,无需任何第三方库。
<template>
<div class="box">主题色方块</div>
<button @click="changeColor">切换颜色</button>
</template>
<script setup>
import { ref } from 'vue'
const themeColor = ref('#3eaf7c')
function changeColor() {
themeColor.value = themeColor.value === '#3eaf7c' ? '#e74c3c' : '#3eaf7c'
}
</script>
<style scoped>
.box {
width: 100px;
height: 100px;
background-color: v-bind(themeColor);
transition: background-color 0.3s;
}
</style>
点击按钮后,方块背景色会平滑过渡。Vue 在编译阶段会将 v-bind(themeColor) 转换为类似 var(--c-xxxxxx) 的 CSS 变量,并在运行时注入到组件根元素上。当 themeColor 变化时,CSS 变量值随之更新,浏览器自动重绘对应样式。
v-bind in CSS 的优势:
- 零额外依赖:无需安装第三方库,直接使用 Vue SFC 语法。
- 保留标准 CSS 体验:依然在
style标签内写 CSS,支持 PostCSS、自动补全、lint 等工具链。 - 运行时动态响应:样式能像模板绑定一样自动跟随数据变化,完美贴合 Vue 的响应式系统。
- 性能友好:底层基于 CSS 变量更新,不产生额外的 DOM 计算或重新编译样式表,开销极小。
社区方案:当需要更强的 CSS-in-JS 能力时
如果你的项目有更激进的需求,比如需要利用 JS 生成主题系统、需要在 JS 侧直接操作样式对象,也可以考虑以下零运行时的 CSS-in-JS 方案(它们编译结果大多是静态 CSS 文件,与 Vue 的集成更像工具链配合):
- Vanilla Extract:通过
createTheme、style等 API 在.css.ts文件中定义样式,编译时生成原子 CSS 类名和变量文件。它需要配合 Vite 插件使用,与 Vue 的集成虽不是官方维护,但有社区适配示例。 - Stitches:提供接近 styled-components 的开发者体验,但也支持零运行时模式。由于它核心是为 React 设计,Vue 中使用需要通过第三方绑定或工程化手段,学习成本偏高。
在实际 Vue 项目中,绝大多数动态样式场景用 v-bind in CSS 即可覆盖。只有在构建大型设计系统、或团队已有 CSS-in-JS 技术积累且需要跨框架复用时,才会慎重评估上述社区方案。
实用建议
- 优先使用
<style scoped>+ CSS 变量:对于大部分组件,scoped 已经足够,配合 CSS 变量可以实现多主题切换等动态能力。 - 需要绑定额外状态时,用
v-bindin CSS:比如根据用户偏好动态调整字号、根据数据状态改变按钮颜色等,比手动操作 class 或内联 style 更清晰。 - 避免重运行时方案:像 Emotion、styled-components 这类依赖运行时生成
<style>标签的方案,在 Vue 中没有官方支持,强行集成会增加包体积和样式闪烁风险,收益却不高。 - 复杂主题系统优先考察设计令牌(Design Tokens):利用 Pinia 存储主题变量,通过 CSS 自定义属性下发,是目前最主流、最稳健的 Vue 主题管理方式。
总的来说,CSS-in-JS 在 Vue 生态中的适配不是“另起炉灶”,而是“官方内置化”——v-bind in CSS 就是 Vue 对这个理念的务实回应。它让你在享受响应式便利的同时,依然能够书写标准的、可维护的 CSS。