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

相关推荐
子兮曰21 小时前
jev-ultrafast 深度解析:7 秒订机票的浏览器 Agent 是如何炼成的
前端·后端·agent
子兮曰21 小时前
Jev 爆发一周:7 秒 Agent 背后的 System One 生态与三场争议
前端·后端·ai编程
前端小万21 小时前
写公众号赚了 3000 块后,我做了一款叫 "一键成稿" 的软件
前端·微信小程序
爱勇宝21 小时前
ZCode 开源 24 小时:一份没有历史的账本,回答不了"有没有偷代码"
前端·后端·chatglm (智谱)
A黄俊辉A21 小时前
uniapp webview中实现 app和内嵌的H5双向通信
vue.js·json
三十而立洋21 小时前
Cookie 详解:从产生到安全,一次讲透
前端·javascript
卡布鲁1 天前
把一个 Vite + Vue3 应用塞进 qiankun (React + Umi3) 主站:十个坑的复盘
前端·javascript·react.js
honkun61 天前
vue 表格组件 vxe-table 配置 ajax 请求自动加载数据与表单查询
vue.js·vxe-table
kybs19911 天前
全球灾害数据分析可视化 毕业设计-附源码66794
vue.js·spring boot·mysql·安全·django·c#·asp.net
汉堡大王95271 天前
Jev:不是聊天机器人, 而是一个智能 if 语句
前端·人工智能·后端