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

相关推荐
vx_bisheyuange4 分钟前
基于SpringBoot的便利店信息管理系统
前端·javascript·vue.js·毕业设计
晚烛5 分钟前
智启工厂脉搏:基于 OpenHarmony + Flutter 的信创工业边缘智能平台构建实践
前端·javascript·flutter
Zsnoin能7 分钟前
都快2026了,还有人不会国际化和暗黑主题适配吗,一篇文章彻底解决
前端·javascript
两个西柚呀9 分钟前
es6和commonjs模块化规范的深入理解
前端·javascript·es6
www_stdio9 分钟前
爬楼梯?不,你在攀登算法的珠穆朗玛峰!
前端·javascript·面试
光影少年10 分钟前
RN vs Flutter vs Expo 选型
前端·flutter·react native
风止何安啊17 分钟前
🚀别再卷 Redux 了!Zustand 才是 React 状态管理的躺平神器
前端·react.js·面试
鹿角片ljp22 分钟前
Spring Boot Web入门:从零开始构建web程序
前端·spring boot·后端
向下的大树28 分钟前
Vue 2迁移Vue 3实战:从痛点到突破
前端·javascript·vue.js
我很苦涩的29 分钟前
原生小程序使用echarts
前端·小程序·echarts