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 结构,就可能匹配到。这会让样式的 影响范围变大,也更难控制。

相关推荐
kyriewen7 小时前
我手写了一个 EventEmitter,面试官追问了 6 个问题——第 4 个我没答上来
前端·javascript·面试
IT_陈寒7 小时前
Java的Date类又坑了我一次,改用时间戳真香
前端·人工智能·后端
小林攻城狮8 小时前
使用 Transport 节流解决 Vercel AI SDK 流式渲染卡死问题
前端·react.js
前端缘梦8 小时前
告别 TS 运行时类型漏洞!Zod 完整入门实战教程(前端 / 全栈必备)
前端·react.js·全栈
the_answer8 小时前
Webpack vs Vite 深度对比分析
前端·webpack
转转技术团队8 小时前
验证码识别实战:前端不写页面,改训模型了?
前端
MomentYY8 小时前
Temperature:AI 的“脑洞旋钮”
前端·llm·ai编程
远航_9 小时前
OpenSpec 完整详细介绍
前端·后端
召钱熏9 小时前
状态枚举正确≠渲染正确:一个语音按钮的状态机边界修复实录
android·前端
SkyWalking中文站9 小时前
认识 Horizon UI · 1/17:SkyWalking 新一代可观测性控制台
运维·前端·监控