Vue 单文件组件(.vue)把模板、逻辑和样式封装在一起。写法上,你可以继续使用自己熟悉的 CSS 技术,Vue 额外提供了一个非常实用的能力:样式作用域隔离。
在 Vue 中写原生 CSS
直接在组件的 <style> 标签里写标准 CSS,这些样式默认是全局生效的——它会影响到当前页面所有匹配的元素,和写在普通 .css 文件里没有区别。
<template>
<div class="demo-box">
<p class="title">我是标题</p>
</div>
</template>
<script setup>
// 组件逻辑
</script>
<style>
/* 这段样式是全局的,会作用到整个应用内所有 .title 元素 */
.demo-box .title {
color: #333;
font-size: 18px;
}
</style>
对于小型项目或原型开发,这种方式最快。但如果你有多个组件都定义了 .title,就很容易互相污染。
使用 SCSS / Less 预处理器
Vue 支持通过 lang 属性无缝切换成 SCSS(Sass)或 Less,前提是项目里安装了对应的预处理器依赖。
用 Vite 安装 SCSS:
npm install -D sass
在组件中使用 SCSS:
<style lang="scss">
$primary: #3b82f6;
.demo-box {
padding: 1rem;
border: 1px solid $primary;
.title {
color: $primary;
&:hover {
color: darken($primary, 10%);
}
}
}
</style>
Less 同理,安装 less 后使用 lang="less" 即可。预处理器的变量、嵌套、混入等特性让复杂样式管理更高效,并且和原生 CSS 的书写体验一致。
scoped 作用域样式:解决组件样式隔离
Vue 的多组件协作中,样式冲突是常见问题。Vue 提供了 scoped 属性 来将样式锁定在当前组件内。
<template>
<div class="box">
<p class="text">这段文字只受当前组件样式影响</p>
</div>
</template>
<script setup>
// 逻辑
</script>
<style scoped>
.box {
background: #f0f0f0;
padding: 1rem;
}
.text {
color: red;
}
</style>
原理(只需理解效果):
- Vue 在编译时会给当前组件的每个DOM元素添加一个唯一的自定义属性,如
data-v-f3f3eg9。 - 同时将
<style scoped>中的选择器改写成类似.text[data-v-f3f3eg9]。 - 这样一来,
.text的样式只作用于带有对应属性的 DOM 元素,也就是当前组件的元素。
开发者不需要关心这个属性怎么生成,只需要加上 scoped,样式就不会泄露到子组件或兄弟组件中。
scoped 的注意事项:
- 不影响子组件根元素
如果你在父组件中想用 scoped 样式修改子组件的内部元素,默认是穿透不了的。例如:
<!-- 父组件 -->
<template>
<ChildComponent class="child-wrapper" />
</template>
<style scoped>
/* 无法影响 Child 内部的 p 标签 */
.child-wrapper p { color: blue; }
</style>
因为子组件内部的 p 没有父组件的 data-v-xxx 属性。
- 深度选择器
:deep()
当确实需要从父组件覆盖子组件内部样式时(如修改第三方UI组件库的某个元素),使用 :deep() 穿透作用域:
<style scoped>
/* 能够选中 .child-wrapper 下所有 p,包括子组件内的 p */
.child-wrapper :deep(p) {
color: blue;
}
</style>
这会在编译后生成类似 .child-wrapper[data-v-xxx] p 的选择器,让样式作用到子组件内部的元素。
- 性能影响
scoped 会在选择器上追加属性,选择器越长可能轻微影响匹配速度,但在绝大多数应用中感知不到。它的便利性远大于性能开销。
最佳实践建议
- 全局与局部分离:将全局的 CSS 重置、通用工具类放在非
scoped的<style>块或独立的全局样式文件中;每个组件的业务样式使用scoped保护。 - 结合 CSS Modules 或原子化 CSS:如果组件样式极其复杂且需要严格隔离,也可以配合 CSS Modules(
<style module>)或 Tailwind CSS / UnoCSS 等原子化方案,灵活选择。 - 命名约定辅助:即使有
scoped,为组件根元素添加一个独特的 class 名称(如.user-profile-card)依然是好习惯,方便调试和阅读。
Vue 的样式处理既尊重了开发者已有的 CSS 技能,又通过 scoped 解决了组件化开发中最让人头疼的样式冲突问题。你不需要学习新语法,只需要知道“这个组件的样式写在这里,它不会跑出去捣乱”。