本节摘要:组件化的另一半是样式隔离——让各组件自己的样式不互相侵蚀。Vue 靠 scoped 开箱即用,React 则有 CSS 模块、CSS-in-JS 等方案。本节对照这三套思路,讲清隔离原理与适用场景,并给出"先算作用域再动手"的判断依据。
本节阅读目标
阅读完本节,你应当能够:
CSS 默认全局生效。当两个组件都定义了一个同名 class,先后引入的样式可能互相覆盖——A 组件的红色标题被 B 的蓝色覆盖,改一处炸一片。组件化要解决"代码隔离",样式隔离是第一道关卡。
Vue 单文件组件里的 scoped 样式把选择器都加上一个唯一属性标识,让它们只在当前组件生效:
<style scoped> .badge { color: blue; font-weight: bold; } </style>
开箱即用,无需配置,是 Vue 组件样式的默认盘。它的局限是 scope 通常不穿透子组件深度修改,要用全局或深选择器时才需要额外声明。
React 本身不管样式,交由社区方案。两种主流:
// CSS Modules 思路:模块化类名自动唯一 import styles from './Card.module.css'; export default function Card() { return <article className={styles.card}>内容</article>; }
两者都实现了"类名唯一化",区别在于样式的存放位置与动态化能力。
| 方案 | 隔离方式 | 优势 | 局限 |
|---|---|---|---|
| Vue scoped | 属性选择器 | 开箱即用无配置 | 更深度透传子组件麻烦 |
| CSS 模块 | 类名哈希化 | 用普通 CSS 也隔离 | 需构建改造类名 |
| CSS-in-JS | 样式进 JS | 动态样式强、随组件走 | 运行时开销、学习成本 |
记一条直觉:先保证隔离开,再谈美观。隔离是安全边界,用错方案顶多是体验打折,用错作用域是程序行为错乱。

隔离方案不能只看原理,还要看日常维护一个组件时是否顺手。scoped 的体验是"零思考"——你正常写 class 就行,库替你加属性,改属性名也不涉及跨文件引用,风险最低。CSS 模块的体验是"类名要改外观":你必须用对象导出而不是字符串类名,IDE 提示更友好,但初学时会多一道换算。
CSS-in-JS 的体验是前两者里自由度最高的,也是心智最重的:样式与逻辑同文件,主题、动态值几乎是免密接入,可一旦团队里有人不熟这套,review 时容易各执一词。比稿时一大衡量标准是"团队默认值是什么"——如果大家熟普通 CSS,CSS 模块是拉平成本最低的;如果团队愿意为动态主题买单,CSS-in-JS 则更顺。方案没有绝对答案,答案在"团队熟悉度 + 动态需求"的交叉点。
隔离解决"互相串",动态化解决"跟着状态变",两者往往要同时面对。拿"暗色/亮色"主题举例:一个按钮要在主题间切换颜色,最粗的做法是在模板里写满 theme === 'dark' ? '#111' : '#fff' 这样的三连字面量,看着乱、改起来要翻遍每个组件。更稳的做法是把配色抽成一份设计令牌(token),让每个组件都引用令牌名字,而不是散落的颜色字面量。
// tokens.js —— 一套颜色令牌,主题只管换这一份 export const tokens = { light: { bg: '#fff', text: '#111', accent: '#0af' }, dark: { bg: '#111', text: '#eee', accent: '#0af' }, };
组件里(无论 React 的 CSS-in-JS 还是 Vue 的 scoped + 类名切换)只认 accent、bg 这类语义名,主题一变,只有 tokens 这一处在动。把"某组件是哪种颜色"和"整套配色长什么样"解耦,正是样式治理里仅次于隔离的第二件事。隔离管住"我的样式不滥伤别人",令牌管住"改主题不必把所有组件翻个底朝天"。两件套齐了,样式这一战才算真正守住。
样式隔离挡掉外围侵蚀,下一节是组件世界最烧脑的一课——复用抽象。当多个组件有重复逻辑,靠什么抽象让它们合而为一?高阶组件、混入、自定义 Hook、组合式函数,逐个比稿。