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 中处理样式穿透的标准方式,理解其工作原理有助于更好地使用和调试样式。

相关推荐
brien920 分钟前
为什么模型标称 1M,Google Antigravity 聊到 256K 却突然失忆?我们给它做了个原生行内监控圆环
前端
修炼的dance35 分钟前
录屏关键帧间隔多少合适?六档实测,2秒到10秒小了75.6%
前端
Lank_M41 分钟前
关键帧从1秒拉到10秒,录屏小87.3%,摄像头小10.8%
前端
补码缩写43 分钟前
用 ffprobe 拆录屏,八成字节在 29 个关键帧上
前端
百思不得小李1 小时前
一个复杂的动态表单是如何实现的
前端·设计模式
陈随易1 小时前
Cmder 停更 17 个月,2.0 支持 Windows Terminal
前端·后端·程序员
风骏时光牛马1 小时前
前沿视界
前端
kyriewen1 小时前
我扫了 814 个依赖,97 个还敢要 Node 10
前端·javascript·程序员
陈随易2 小时前
3000 万人在用的翻译插件,重写一遍就是一门生意
前端·后端·程序员
IT_陈寒2 小时前
Vue的这个响应式陷阱,差点让我加班到天亮
前端·人工智能·后端