Sass/Less、BEM、CSS Modules、Scoped 与 CSS-in-JS 分别解决什么?怎样治理样式冲突?
先理解原理和示例,再练习完整口述与追问。
回答要点
预处理器提供变量、嵌套、混入等编译期能力;BEM 用命名约定降低冲突与优先级混乱;CSS Modules 和 Vue scoped 在构建时生成唯一类名或属性选择器实现作用域;CSS-in-JS 让样式与组件逻辑共存。全局治理还要靠设计令牌、Cascade Layers 和 Stylelint。
原理拆解
Sass/Less 在构建时编译成 CSS,变量值固定在产物中;原生 CSS 变量在运行时生效并参与层叠,主题切换更适合用 CSS 变量。原生 CSS 已支持嵌套,PostCSS 则负责加前缀、语法降级等后处理。
BEM 写成 block__element--modifier,每个选择器都是单个类,优先级扁平。CSS Modules 把 .title 编译成带哈希的类名,从根本上避免同名冲突。Vue scoped 给组件元素加 data-v-hash 属性,并把选择器改写为 .title[data-v-hash];子组件根元素同时带父组件的属性,所以父组件能影响子组件根,深层元素需要 :deep()。Shadow DOM 提供浏览器级隔离。styled-components 等运行时 CSS-in-JS 有生成样式的开销和 SSR 收集问题,零运行时方案在构建期提取 CSS。
示例与推演
@layer reset, vendor, components, utilities;
@layer vendor { @import url('element-plus.css'); }
@layer components { .card__title--active { color: var(--color-primary); } }
Cascade Layers 让后声明的层优先,层内再比较选择器优先级,因此业务组件不必写更长的选择器去覆盖组件库样式。
易错点
scoped 不是安全隔离,全局样式仍能影响组件;动态拼接类名会被 PurgeCSS 等工具误删;用 !important 解决冲突只会让下一次覆盖更难。
参考回答可直接练习口述
预处理器解决的是编写效率,比如变量、嵌套、混入;BEM 用命名约定避免冲突并保持优先级扁平;CSS Modules 和 Vue scoped 在构建时生成唯一类名或属性选择器,让样式只作用于组件;CSS-in-JS 让样式跟随组件,但要注意运行时开销。治理大规模样式时,我用设计令牌和 CSS 变量统一主题,用 Cascade Layers 控制第三方库和业务样式的覆盖顺序,再用 Stylelint 约束写法。