Vue scoped 样式不生效的一个坑:CSS 选择器与 class 合并机制

文章目录

  • [Vue scoped 样式不生效的一个坑:CSS 选择器与 class 合并机制](#Vue scoped 样式不生效的一个坑:CSS 选择器与 class 合并机制)
    • 1.相关知识
      • [1.1 后代选择器 vs 同元素多 class 选择器](#1.1 后代选择器 vs 同元素多 class 选择器)
      • [1.2 Vue class 合并机制](#1.2 Vue class 合并机制)
      • [1.3 scoped + :deep 穿透原理](#1.3 scoped + :deep 穿透原理)
      • [1.4 小结:空格 vs 无空格](#1.4 小结:空格 vs 无空格)
    • [2. demo实战](#2. demo实战)

Vue scoped 样式不生效的一个坑:CSS 选择器与 class 合并机制

1.相关知识

1.1 后代选择器 vs 同元素多 class 选择器

后代选择器

javascript 复制代码
.a .b

意思:选中 a 里面的 b

结构如下:

javascript 复制代码
<div class="a">
  <div class="b"></div>
</div>

同元素多 class 选择器

复制代码
.a.b

选中同时具有 a 和 b 的元素

结构如下:

javascript 复制代码
<div class="a b"></div>

一个空格,修改样式的含义就改变了

1.2 Vue class 合并机制

Vue 会把父组件传入的 class 合并到子组件根元素

代码例子如下:

javascript 复制代码
父组件
<ChunkUploadTrigger class="upload-area" />

子组件:
<div class="chunk-upload-trigger">

最终 DOM:
<div class="chunk-upload-trigger upload-area">

1.3 scoped + :deep 穿透原理

Vue scoped CSS 穿透

当写

javascript 复制代码
<style scoped>
.box {
  color: red;
}
</style>

Vue 编译后会变成类似:
<div class="box" data-v-123abc>
  
CSS 也会变成:
.box[data-v-123abc] {
  color: red;
}

CSS 只会影响 当前组件

举例来说:

子组件 Child.vue

javascript 复制代码
<template>
  <div class="child-box">
    子组件内容
  </div>
</template>

<style scoped>
.child-box {
  border: 2px solid gray;
  padding: 20px;
}
</style>

父组件 Parent.vue

javascript 复制代码
<template>
  <Child />
</template>

<script setup>
import Child from "./Child.vue"
</script>

<style scoped>
.child-box {
  border-color: red;
}
</style>

样式不会生效

原因如下:

编译后 DOM 变成:

javascript 复制代码
<div class="child-box" data-v-child></div>

父组件 CSS:

javascript 复制代码
.child-box[data-v-parent]

但是 DOM 是:

javascript 复制代码
data-v-child
不是:
data-v-parent 所以匹配不到

正确写法:

javascript 复制代码
<style scoped>
:deep(.child-box) {
  border-color: red;
}
</style>

// 忽略 scoped 限制
// 直接选中 child-box

1.4 小结:空格 vs 无空格

来一个例子:

javascript 复制代码
.upload-area :deep(.chunk-upload-trigger)
//这在 CSS 里的意思是:
upload-area 里面的 chunk-upload-trigger

也就是要求 Dom

javascript 复制代码
<div class="upload-area">
  <div class="chunk-upload-trigger"></div>
</div>

但是如果 Dom 结构是下面这样的

javascript 复制代码
<div class="chunk-upload-trigger upload-area"></div>

意思是同时拥有两个 class 的元素

javascript 复制代码
:deep(.chunk-upload-trigger.upload-area)

没有空格

空格 = 父子关系,没空格 = 同一个元素

2. demo实战

子组件:

html 复制代码
<template>
    <div class="child-box">
      子组件
      <div class="content">
        内容区域
      </div>
    </div>
  </template>
  
  <script setup lang="ts">
  </script>
  
  <style scoped>
  .child-box {
    border: 2px solid #ccc;
    padding: 20px;
  }
  
  .content {
    margin-top: 10px;
    padding: 10px;
    background: #f5f5f5;
  }
  </style>

子组件DOM实际是:

html 复制代码
<div class="child-box">
  子组件
  <div class="content"></div>
</div>

父组件 Parent.vue

html 复制代码
<template>
  <div class="wrapper">

    <h3>案例1:class 透传</h3>
    <Child class="a b" />

  </div>
</template>

<script setup>
import Child from "./Child.vue"
</script>

<style scoped>
</style>

Vue 会把 class 合并到子组件根节点。

此时真实的DOM

javascript 复制代码
<div class="child-box a b">
  子组件
  <div class="content"></div>
</div>

练习1:.a.b(同一个元素)

在 Parent.vue 写:

css 复制代码
:deep(.a.b) {
  border: 3px solid red;
}

练习2:.a .b(后代选择器)

修改 Parent.vue:

html 复制代码
<template>
  <div class="wrapper">

    <div class="a">
      <Child class="b" />
    </div>

  </div>
</template>

真实 DOM:

html 复制代码
<div class="a">
   <div class="child-box b"></div>
</div>

推荐写法:

javascript 复制代码
.a :deep(.b) {
  border: 3px solid blue;
}

含义

javascript 复制代码
当前组件中的 .a
    ↓
穿透到子组件中的 .b

Vue 编译后的效果类似:
.a[data-v-xxxx] .b
  • .a 仍然受 scoped 限制
  • .b 允许 穿透到子组件

这种写法同样可以生效,但作用范围更大。

javascript 复制代码
:deep(.a .b){
  border: 3px solid red;
 }
  • .a 和.b 都脱离了 scoped 约束。

  • 因此只要在当前组件 DOM 树内部出现 .a .b 结构,就可能匹配到。这会让样式的 影响范围变大,也更难控制。

相关推荐
tedcloud12311 小时前
UI-TARS-desktop部署教程:构建AI桌面自动化系统
服务器·前端·人工智能·ui·自动化·github
UXbot14 小时前
AI原型设计工具如何支持团队协作与快速迭代
前端·交互·个人开发·ai编程·原型模式
ZC跨境爬虫14 小时前
跟着MDN学HTML_day_48:(Node接口)
前端·javascript·ui·html·音视频
PieroPc16 小时前
CAMWATCH — 局域网摄像头监控系统 Fastapi + html
前端·python·html·fastapi·监控
巴巴博一17 小时前
2026 最新:Trae / Cursor 一键接入 taste-skill 完整教程(让 AI 前端告别“AI 味”)
前端·ai·ai编程
kyriewen17 小时前
半夜三点线上崩了,AI替我背了锅——用AI排错,五分钟定位三年老bug
前端·javascript·ai编程
kyriewen17 小时前
我让 AI 当了 24 小时全年无休的“毒舌考官”
前端·ci/cd·ai编程
hexu_blog17 小时前
vue+java实现图片批量压缩
java·前端·vue.js
IT_陈寒18 小时前
为什么你应该学习JavaScript?
前端·人工智能·后端
lifejump18 小时前
Empire(帝国)CMS 7.5 XSS注入
前端·安全·xss