scoped样式只作用于当前组件静态编译的DOM元素,对v-html、Teleport挂载内容及子组件内部结构无效,因其依赖data-v-xxxxxx属性匹配,而这些场景下该属性缺失或不匹配。

scoped 能隔离样式,但不是加了就自动生效——它只作用于当前组件**静态编译出的 DOM 元素**,对子组件内部、v-html、Teleport 挂载内容完全无效。
为什么加了 scoped 样式还是泄漏或失效?
根本原因是 Vue 编译时只给模板里「写死」的元素加 data-v-xxxxxx 属性,并重写 CSS 选择器(如 .btn → .btn[data-v-f3f42f0a])。所以:
-
v-html渲染的内容不带该属性,选择器无法匹配 - 第三方组件(如
<el-dialog></el-dialog>)内部结构不在当前模板中,不会被打标 -
Teleport到body的弹层,DOM 已脱离当前组件树,属性丢失 - 子组件的根元素虽在模板中出现,但它的子节点仍属子组件作用域,父组件
scoped不穿透
:deep() 穿透子组件样式的正确写法
Vue 3 推荐用 :deep() 修改子组件或第三方组件内部样式,但它有硬性约束:
Vue 3 组合式 API 与 最佳实践,涵盖代码组织、useXxx 模式、组合式函数设计、Store 集成、响应式优化及功能抽取。
- 必须有前缀选择器(不能单独写
:deep(.el-input__inner)),例如:.form :deep(.el-input__inner) -
:deep()只支持后代选择器语法,括号不能省,:deep .el-input__inner(无括号)在部分构建链路中不识别 - 避免宽泛匹配:
:deep(div)会污染所有div,应精确到类名或组件名 - 若子组件暴露了
class-prefix或popper-class这类 prop(如el-date-picker的popper-class),优先用它传入自定义类,比:deep()更可控
scoped 和 module 怎么选?
二者机制不同,不能混用(<style scoped module></style> 无效):
立即学习“前端免费学习笔记(深入)”;
-
scoped:靠属性选择器,DevTools 可见data-v-xxx,适合常规组件,调试直观 -
module:类名编译为哈希变量(如_header_123),JS 中可通过$style.header引用,适合需动态切换类名的场景 - 需要在
setup()里根据状态拼接多个类(如[$style.active, $style.disabled]),选module - 全局重置(如
body、html)、CSS 变量定义,应放在独立的<style></style>块,且置于最前,避免被scoped覆盖
真正容易被忽略的是:scoped 的选择器膨胀在嵌套多层(尤其 Sass 的 &)时会显著拖慢 CSS 解析,而 :deep() 若滥用,等于主动放弃隔离——它不报错,但会让样式维护变成猜谜游戏。

















