Vue 深度选择器 `:deep` 使用说明

Vue 深度选择器 :deep 使用说明

一、基本概念

1. 什么是 :deep

:deep 是 Vue 3 提供的深度选择器 (Deep Selector),用于在 scoped 样式中穿透到子组件的元素。

2. 为什么需要 :deep

在 Vue 中,<style scoped> 会为每个组件的样式添加唯一的 data-v-* 属性,确保样式只作用于当前组件。但有时候我们需要修改子组件(特别是第三方组件库如 Element Plus)的样式,这时就需要使用 :deep 来穿透样式作用域。

二、工作原理

1. Scoped 样式的作用机制

普通 scoped 样式:

vue 复制代码
<style lang="scss" scoped>
.my-class {
    color: red;
}
</style>

编译后:

css 复制代码
[data-v-7ba5bd90] .my-class[data-v-7ba5bd90] {
    color: red;
}
  • 只会匹配当前组件内的 .my-class
  • 不会匹配子组件中的元素

2. 使用 :deep 穿透样式

vue 复制代码
<style lang="scss" scoped>
:deep {
    .el-form-item__content {
        align-items: self-start;
    }
}
</style>

编译后:

css 复制代码
[data-v-7ba5bd90] .el-form-item__content {
    align-items: self-start;
}
  • 选择器中的 .el-form-item__content 不会被添加 data-v-* 属性
  • 可以匹配子组件中的 .el-form-item__content 元素

3. 关键区别对比

方式 编译前 编译后 能否匹配子组件
普通 scoped .my-class { } [data-v-xxx] .my-class[data-v-xxx] { } ❌ 否
使用 :deep :deep { .my-class { } } [data-v-xxx] .el-form-item__content { } ✅ 是

三、实际应用场景

1. 修改 Element Plus 组件样式

场景: 修改表格搜索表单中表单项的对齐方式

vue 复制代码
<template>
    <zh-table>
        <!-- zh-table 内部使用 Element Plus 的 el-form-item -->
    </zh-table>
</template>

<style lang="scss" scoped>
:deep {
    .el-form-item__content {
        align-items: self-start; /* 顶部对齐 */
    }
}
</style>

DOM 结构:

html 复制代码
<!-- index.vue 的根元素 -->
<div data-v-7ba5bd90>
    <!-- zh-table 组件 -->
    <div data-v-abc123>
        <el-form>
            <el-form-item>
                <div class="el-form-item__content">
                    <!-- 👈 被匹配到 -->
                    <input />
                </div>
            </el-form-item>
        </el-form>
    </div>
</div>

CSS 匹配过程:

css 复制代码
/* 编译后的选择器 */
[data-v-7ba5bd90] .el-form-item__content {
    align-items: self-start;
}

/* 匹配过程:
   1. 找到带有 data-v-7ba5bd90 的元素(index.vue 的根元素)
   2. 在该元素的后代中查找 .el-form-item__content
   3. 找到子组件中的 .el-form-item__content 元素
   4. 应用样式
*/

2. 修改第三方组件库样式

vue 复制代码
<template>
    <el-input />
</template>

<style lang="scss" scoped>
:deep {
    .el-input__wrapper {
        border-radius: 6px;
        background-color: #f5f5f5;
    }

    .el-input__inner {
        height: auto;
        color: #000000e0;
    }
}
</style>

3. 嵌套使用 :deep

vue 复制代码
<style lang="scss" scoped>
.zh-default-input {
    :deep {
        .el-input__wrapper {
            border-radius: 6px !important;
        }
        .el-input__inner {
            height: auto;
        }
    }
}
</style>

编译后:

css 复制代码
[data-v-xxx] .zh-default-input .el-input__wrapper {
    border-radius: 6px !important;
}
[data-v-xxx] .zh-default-input .el-input__inner {
    height: auto;
}

四、语法说明

1. 基本语法

scss 复制代码
:deep(选择器) {
    样式规则
}

// 或者使用块语法
:deep {
    选择器 {
        样式规则
    }
}

2. 两种写法对比

写法1:函数式(推荐)

scss 复制代码
:deep(.el-form-item__content) {
    align-items: self-start;
}

写法2:块语法

scss 复制代码
:deep {
    .el-form-item__content {
        align-items: self-start;
    }
}

两种写法编译结果相同,块语法更清晰易读。

3. 组合使用

scss 复制代码
:deep {
    .el-input__wrapper,
    .el-textarea__inner {
        border-radius: 6px !important;
    }

    .el-input.is-disabled .el-input__wrapper {
        background-color: #fafafa;
    }
}

五、为什么能找到子组件中的元素?

1. CSS 选择器的作用域

:deep 生成的选择器是后代选择器(Descendant Selector):

css 复制代码
[data-v-7ba5bd90] .el-form-item__content

这个选择器表示:

  • data-v-7ba5bd90 元素的后代中
  • 查找 .el-form-item__content 元素
  • 不限制层级,可以匹配任意深度的后代

2. DOM 树结构

css 复制代码
index.vue (data-v-7ba5bd90)          ← 样式作用域的根
└── zh-table (data-v-abc123)         ← 子组件
    └── SearchForm                   ← 子组件的子组件
        └── el-form                  ← Element Plus 组件
            └── el-form-item
                └── .el-form-item__content  ← 被匹配到

3. 样式穿透机制

  • :deep 让选择器中的目标类不被添加 data-v-* 属性
  • 因此可以匹配到子组件中的同名类
  • 通过后代选择器在 DOM 树中查找匹配的元素

六、浏览器中的实际效果

1. 实际 DOM 结构

html 复制代码
<!-- 浏览器渲染后的 DOM -->
<div data-v-7ba5bd90 class="...">
    <div data-v-abc123 class="table-search">
        <form class="el-form table-search--left">
            <div class="el-form-item">
                <div class="el-form-item__content">
                    <!-- 👈 被匹配 -->
                    <input class="el-input__inner" />
                </div>
            </div>
        </form>
    </div>
</div>

2. CSS 规则应用

css 复制代码
/* 编译后的 CSS */
[data-v-7ba5bd90] .el-form-item__content {
    align-items: self-start;
}

匹配过程:

  1. 浏览器找到所有带有 data-v-7ba5bd90 属性的元素
  2. 在这些元素的后代中查找 .el-form-item__content
  3. 找到匹配的元素并应用样式

七、注意事项

1. 作用域限制

:deep 仍然受到父组件 scoped 的作用域限制:

scss 复制代码
/* ✅ 正确:可以匹配子组件 */
:deep {
    .el-form-item__content {
        align-items: self-start;
    }
}

/* ❌ 错误:无法匹配子组件 */
.el-form-item__content {
    align-items: self-start; /* 只会匹配当前组件 */
}

2. 性能考虑

:deep 选择器会遍历整个 DOM 树,应该:

  • 推荐:使用具体的选择器,减少匹配范围
scss 复制代码
:deep {
    .el-form-item__content {  /* 具体类名 */
        ...
    }
}
  • ⚠️ 避免:使用过于宽泛的选择器
scss 复制代码
:deep {
    div {  /* 太宽泛,可能影响很多元素 */
        ...
    }
}

3. 优先级问题

:deep 生成的样式优先级可能不够高,必要时使用 !important

scss 复制代码
:deep {
    .el-input__wrapper {
        border-radius: 6px !important; /* 确保覆盖默认样式 */
    }
}

4. 替代方案

如果只需要修改少量样式,也可以使用:

方案1:不使用 scoped

vue 复制代码
<style lang="scss">
/* 不使用 scoped,样式会全局生效 */
.el-form-item__content {
    align-items: self-start;
}
</style>

方案2:使用全局样式文件

scss 复制代码
// styles/element.scss
.el-form-item__content {
    align-items: self-start;
}

八、常见使用场景

1. 修改 Element Plus 组件样式

vue 复制代码
<style lang="scss" scoped>
:deep {
    .el-button {
        border-radius: 6px;
    }

    .el-input__wrapper {
        background-color: #f5f5f5;
    }

    .el-select__wrapper {
        border-radius: 6px;
    }
}
</style>

2. 修改自定义组件样式

vue 复制代码
<template>
    <zh-table>
        <!-- 需要修改 zh-table 内部的样式 -->
    </zh-table>
</template>

<style lang="scss" scoped>
:deep {
    .table-search {
        padding: 16px;
    }

    .el-form-item {
        margin-bottom: 0;
    }
}
</style>

3. 响应式样式穿透

scss 复制代码
:deep {
    .el-form-item__content {
        @media (max-width: 768px) {
            flex-direction: column;
        }
    }
}

九、Vue 2 vs Vue 3

Vue 2 语法

scss 复制代码
/* Vue 2 使用 /deep/ 或 >>> */
/deep/ .el-form-item__content {
    align-items: self-start;
}

/* 或者 */
>>> .el-form-item__content {
    align-items: self-start;
}

Vue 3 语法

scss 复制代码
/* Vue 3 使用 :deep() */
:deep(.el-form-item__content) {
    align-items: self-start;
}

/* 或者块语法 */
:deep {
    .el-form-item__content {
        align-items: self-start;
    }
}

十、总结

:deep 的核心要点

  1. 作用:穿透 scoped 样式,修改子组件样式
  2. 原理 :编译时不为目标选择器添加 data-v-* 属性
  3. 机制:通过后代选择器在 DOM 树中查找匹配元素
  4. 使用:在需要修改子组件样式时使用
  5. 注意:合理使用,避免过度穿透影响性能

最佳实践

  1. ✅ 优先使用具体的选择器
  2. ✅ 只在必要时使用 :deep
  3. ✅ 注意样式优先级
  4. ✅ 避免过度穿透影响性能
  5. ✅ 考虑使用全局样式文件作为替代方案

适用场景

  • 修改第三方组件库(如 Element Plus)的样式
  • 修改自定义子组件的样式
  • 需要保持 scoped 样式隔离的同时修改子组件

:deep 是 Vue 3 中处理样式穿透的标准方式,理解其工作原理有助于更好地使用和调试样式。

相关推荐
程序媛ing1 小时前
React + ECharts 动态折线图实现
前端·react.js
广州华水科技2 小时前
单北斗GNSS变形监测在地质灾害与基础设施安全中的应用与优势分析
前端
程序员鱼皮2 小时前
又被 Cursor 烧了 1 万块,我麻了。。。
前端·后端·ai·程序员·大模型·编程
孟祥_成都2 小时前
nextjs 16 基础完全指南!(一) - 初步安装
前端·next.js
程序员爱钓鱼2 小时前
使用简单 JSON + 自定义 t 函数实现轻量多语言国际化(无需 next-intl)
前端·javascript·trae
一 乐2 小时前
助农平台|基于SprinBoot+vue的助农服务系统(源码+数据库+文档)
前端·javascript·数据库·vue.js·ecmascript·springboot
vivo互联网技术2 小时前
浅谈 AI 搜索前端打字机效果的实现方案演进
前端·vue·dom
●VON2 小时前
Electron 小游戏实战:太空打砖块(Space Breakout)
前端·javascript·electron
重铸码农荣光2 小时前
深入理解 JavaScript 原型机制:从“如何拿到小米 SU7”说起
前端·javascript