Vue 深度选择器(:deep)完全指北:从“能用”到“用好”
一句话先上车
只要你在<style scoped>
里写过.a >>> .b
、/deep/ .b
或者::v-deep .b
,今天这篇文章都能帮你把它们一次性捋成 Vue 3 官方推荐的:deep()
,并学会“什么时候必须穿透、什么时候最好忍住”。
1. 为什么会有“深度选择器”?
Vue 的 scoped CSS
通过给节点打 data-v-hash
属性实现样式隔离。
编译前:
<style scoped>
.title { color: red; }
</style>
编译后(近似):
.title[data-v-7ba5bd90] { color: red; }
子组件内部的 DOM 不会 带这个属性,于是父组件的样式天然“够不到”子组件内部。
深度选择器就是告诉编译器:“这一段别给我加属性限制,我要穿墙!”
2. 语法全家福(Vue 2 → Vue 3 演进)
版本 | 官方推荐 | 旧写法 / 别名 | 备注 |
---|---|---|---|
Vue 2.6+ | ::v-deep | /deep/ 、>>> | /deep/ 在 css-loader 4+ 已废弃;>>> 原生 CSS 语法,但 sass 会报错 |
Vue 3.x | :deep(<inner-selector>) | ::v-deep 仍向下兼容 | 函数式写法,与 W3C 草案对齐,未来最安全 |
本文后续统一用 Vue 3 写法演示,记住这一个就够:
.a :deep(.b) { /* 样式 */ }
3. 最小可运行示例
<template><!-- 第三方组件 --><el-button class="my-btn">默认按钮</el-button>
</template><style scoped>
/* 把 el-button 内部原生 span 的字号改成 20px */
.my-btn :deep(span) {font-size: 20px;
}
</style>
编译后(近似):
.my-btn[data-v-7ba5bd90] span {font-size: 20px;
}
属性值只加在 .my-btn
上,不再限制 span
,于是成功“穿墙”。
4. 预处理器避坑速查
预处理器 | >>> | /deep/ | ::v-deep | :deep() |
---|---|---|---|---|
sass/scss | ❌ 直接报错 | ❌ 被废弃 | ✅ | ✅(最推荐) |
less | ⚠️ 警告 | ✅ | ✅ | ✅ |
stylus | ✅ | ✅ | ✅ | ✅ |
结论:无论团队用哪种预处理器,直接写 :deep()
永远不会踩坑。
5. 进阶 5 连问
5.1 能写多个层级吗?
.a :deep(.b .c) { color: red; }
可以,编译后只会给 .a
加属性,.b .c
保持原样。
5.2 伪类、伪元素行不行?
.a :deep(.b:hover) { }
.a :deep(.b::before) { }
行,放心写。
5.3 可以动态拼接吗?
.a :deep(.${dynamicClass}) { }
不行! 编译阶段静态分析,只能写死。
5.4 想同时改自身 + 内部?
.a,
.a :deep(.b) {color: red;
}
完全 OK,选择器列表里每一项独立处理。
5.5 深度选择器会降低性能吗?
编译后只是多了一条属性选择器,运行期无差异;
真正影响的是“滥用”导致样式耦合、可维护性下降。
6. 实战:3 个最容易想起“深度选择器”的场景
场景 | 思路 | 示例 |
---|---|---|
覆盖第三方组件 | 直接 deep | .my-dialog :deep(.el-dialog__header) { background: #409eff; } |
递归树/菜单 | 统一改第 N 级 | .tree :deep(.tree-node-3) { font-weight: bold; } |
teleport 到 body 的弹层 | 父组件 scoped 够不到 | .trigger-wrapper :deep(.dropdown) { z-index: 3000; } |
7. 设计原则:先忍住,再穿透
-
能用 props / class / 插槽 就别 deep
子组件自己暴露header-class
、body-style
是最安全的契约。 -
deep 只改“视觉”不改“结构”
只动颜色、间距、字体;别碰定位、布局,防止组件升级 DOM 结构后炸掉。 -
统一文件或统一前缀
团队约定所有 deep 样式集中放在xxx.overrides.vue
或统一加.override--
前缀,方便全局搜索、后续剔除。
8. 一键替换老代码的正则小技巧
VSCode 全局替换
搜索:/deep/\s+
替换::deep(
再手动补右括号 )
,即可把 Vue 2 老项目批量升级成 Vue 3 语法。
9. 总结口诀
scoped 想穿墙,
Vue 3 用:deep()
一行;
伪类伪元素都接得住,
动态拼接会泡汤;
先 props 后 deep,
维护起来不迷茫。
把这篇收藏进浏览器书签,下次再遇到 /deep/
报错或 >>>
被 sass 嫌弃时,直接翻出第 4 节对照表,3 秒改完,岁月静好。祝你写 Vue 永远不再为“样式穿透”头秃!