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

相关推荐
im_AMBER16 小时前
手撕代码之事件委托
前端·javascript·面试
用户81135818812016 小时前
React全家桶笔记(三):React进阶 — 事件处理、表单与生命周期
前端
用户81135818812016 小时前
React全家桶笔记(二):React组件核心 — State、Props、Refs
前端
Jenlybein16 小时前
一文了解 pnpm,并快速上手操作!
前端·javascript·npm
大萝卜呼呼16 小时前
Next.js第二课 - 项目结构详解 - 优栈
前端·next.js
skywalkzf16 小时前
全志 V853 开发:lunch 不显示项目列表问题排查与解决
前端·chrome
踩着两条虫16 小时前
VTJ.PRO 在线应用开发平台的项目模板(Web、H5、UniApp)
前端·低代码·ai编程
云原生指北16 小时前
测试文章标题 - Omnipub 自动发布测试
前端
无责任此方_修行中16 小时前
"JavaScript"这个名字,到底属于谁?一场价值74亿美元的法律战争
前端·javascript·程序员
CharlesY16 小时前
网页排版与编码的隐形神器:HTML字符实体从入门到精通
前端