Vue的scoped样式通过编译时为元素添加唯一data-v-xxx属性并重写CSS选择器为.class[data-v-xxx]实现硬隔离,不作用于v-html、Teleport及子组件内部节点。

在单文件组件(.vue 文件)中用 scoped 是避免类名冲突最直接有效的方式。它不是“加个属性就完事”,而是靠编译时自动注入属性+重写选择器,实现样式作用域硬隔离。
scoped 的基本写法和生效逻辑
只需在 <style> 标签上添加 scoped 属性:
<template>
<div class="card">
<h3 class="title">用户信息</h3>
</div>
</template>
<style scoped>
.card { padding: 16px; border-radius: 8px; }
.title { color: #333; font-size: 18px; }
</style>
Vue 编译后会做两件事:
- 给模板中所有元素自动加上唯一属性,比如
data-v-2f3e4a5b - 把 CSS 选择器改写为带该属性的形式,例如
.card[data-v-2f3e4a5b]
这样,哪怕其他组件也写了 .card,样式也不会互相影响。
立即学习“前端免费学习笔记(深入)”;
哪些地方要注意不生效或出问题
scoped 并非万能,以下情况容易误判或失效:
- 子组件的根元素如果用了和父组件相同的类名(如都叫
.container),父组件的 scoped 样式不会影响子组件内部,但子组件自己的 scoped 样式仍只作用于自身 - 使用
<style scoped>后,无法直接选中子组件内部结构(比如想改<el-input>里的 input 框背景色),需要穿透 -
App.vue或布局类顶层组件通常不建议加 scoped,因为它的样式本意就是全局基础样式
需要穿透子组件时怎么写
当你要覆盖第三方组件(如 Element Plus、Ant Design Vue)内部样式时,必须“穿透” scoped 边界:
- 在普通 CSS 中用
>>>:div >>> .el-input__inner { border-color: red; } - 在 SCSS/Less 中用
::v-deep()或简写/deep/(已弃用但兼容性好):::v-deep .el-input__inner { border-color: red; } - Vue 3 推荐统一用
::v-deep(),且需放在选择器最外层
配合 BEM 命名提升可维护性
scoped 解决了“冲突”,但没解决“可读性”。建议搭配 BEM 命名规范:
- 组件名作为前缀,比如
.user-card__header、.user-card__avatar - 即使去掉 scoped,类名也不易撞车,便于调试和协作
- 命名即语义,别人一眼看出这是 user-card 组件的一部分
不复杂但容易忽略。scoped 是底线,BEM 是加分项,两者一起用,样式才真正可控。


















