人人都会AI编程

原生 CSS / SCSS / Less 与作用域样式 scoped

更新时间:2026-07-10

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 的注意事项

  1. 不影响子组件根元素

如果你在父组件中想用 scoped 样式修改子组件的内部元素,默认是穿透不了的。例如:

   <!-- 父组件 -->
   <template>
     <ChildComponent class="child-wrapper" />
   </template>
   <style scoped>
   /* 无法影响 Child 内部的 p 标签 */
   .child-wrapper p { color: blue; }
   </style>
   

因为子组件内部的 p 没有父组件的 data-v-xxx 属性。

  1. 深度选择器 :deep()

当确实需要从父组件覆盖子组件内部样式时(如修改第三方UI组件库的某个元素),使用 :deep() 穿透作用域:

   <style scoped>
   /* 能够选中 .child-wrapper 下所有 p,包括子组件内的 p */
   .child-wrapper :deep(p) {
     color: blue;
   }
   </style>
   

这会在编译后生成类似 .child-wrapper[data-v-xxx] p 的选择器,让样式作用到子组件内部的元素。

  1. 性能影响

scoped 会在选择器上追加属性,选择器越长可能轻微影响匹配速度,但在绝大多数应用中感知不到。它的便利性远大于性能开销。

最佳实践建议

  • 全局与局部分离:将全局的 CSS 重置、通用工具类放在非 scoped<style> 块或独立的全局样式文件中;每个组件的业务样式使用 scoped 保护。
  • 结合 CSS Modules 或原子化 CSS:如果组件样式极其复杂且需要严格隔离,也可以配合 CSS Modules(<style module>)或 Tailwind CSS / UnoCSS 等原子化方案,灵活选择。
  • 命名约定辅助:即使有 scoped,为组件根元素添加一个独特的 class 名称(如 .user-profile-card)依然是好习惯,方便调试和阅读。

Vue 的样式处理既尊重了开发者已有的 CSS 技能,又通过 scoped 解决了组件化开发中最让人头疼的样式冲突问题。你不需要学习新语法,只需要知道“这个组件的样式写在这里,它不会跑出去捣乱”。