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

相关推荐
脸大是真的好~2 小时前
黑马AI+前端教程 02-视频和音频-超链接-布局标签-表格-文本密码-单选复选框-单个多个文件上传-多行文本-按键-辅助标签
前端
jingling5552 小时前
无需重新安装APK | uni-app 热更新技术实战
前端·javascript·前端框架·uni-app·node.js
遇见小美好y2 小时前
uniapp 实现向下追加数据功能
前端·javascript·uni-app
wuhen_n2 小时前
数据缓存策略:让我们的应用“快如闪电”
前端·javascript·vue.js
wuhen_n2 小时前
自定义指令:为 DOM 操作提供高效的抽象入口
前端·javascript·vue.js
C_心欲无痕2 小时前
前端 PDF 渲染与下载实现
前端·pdf
jiayong232 小时前
可视化流程设计器技术对比:钉钉风格 vs BPMN
java·前端·钉钉
前端不太难2 小时前
Flutter Web / Desktop 为什么“能跑但不好用”?
前端·flutter·状态模式
甘露s2 小时前
新手入门:传统 Web 开发与前后端分离开发的区别
开发语言·前端·后端·web