• 为什么你的Vue会出现样式穿透不生效的情况?
  • 发布于 2个月前
  • 753 热度
    0 评论
  • Jeanks
  • 0 粉丝 25 篇博客
  •   
在日常 Vue 开发中,经常会遇到样式穿透不生效的情况。如果是选择器优先级问题或者穿透写法问题,这种比较好理解,提升优先级调整写法就好,但在 Vue 中有时候需要将 scoped 去掉才能生效,有时候又不能去掉 scoped,有时候因为写法的问题导致不生效,现整理下具体原因,以防后续踩坑。

scoped 与样式穿透的爱恨情仇
1)scoped 的作用:
scoped 的作用是避免样式污染,不加 scoped,书写的样式作用于全局,加之后的样式仅针对当前组件生效。

2)scoped 的原理:
每个添加了 scoped 的组件会被分配一个唯一哈希,通过 CSS 属性选择器实现域划分。

3)demo 示例:
<!-- App.vue -->
<template>
  <RouterView></RouterView>
</template>

<style scoped>
</style>

<!-- layout.vue -->
<template>
  <div class='AdminPage'>
    <div class='app-header-box'>
        <AppHeader/> 
     </div>
    <div class='app-content-box'>
    </div>
  </div>
</template>

<style scoped>
.AdminPage{
  position: relative;
  width: 100%;
  background-color: #F3F2F2;
}

.AdminPage .app-header-box{
  position: fixed;
  width: 100%;
  height: 48px;
  z-index: 100;
}

.AdminPage .app-content-box{
}
</style>
添加了 scoped 的组件,最终渲染效果:
.每个添加了 scoped 的组件渲染出来都会附带一个唯一的属性 data-v-xxx;
.一个组件中的所有标签都会带上同样的 data-v-xxx 属性;
.子组件会带上父组件的 data-v-xxx 属性;
.在使用第三方的 UI 库时,只会为根元素添加 data-v-xxx 属性,子元素中则不会添加;
4)demo 效果:

deep() 做了哪些工作?
deep() 函数会把属性选择器放在最前面。

为什么有时候穿透需要多包裹一层容器
1)demo 示例:
father.vue 嵌套子组件 son.vue
<!-- father.vue -->
<template>
  <div>father</div>
  <Son />
</template>

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

</script>

<style lang="less" scoped>
div{
  color: red;
}
:deep(.second-row){
  color: yellow;
}
</style>

<!-- son.vue -->
<template>
  <div>111</div>
  <div class="second-row">222</div>
  <div>333</div>
</template>
<script></script>

<style scoped>
.second-row{
  color: green
}
</style>
2)demo效果:
按照上述代码,穿透样式并未生效,仅子组件样式生效。

这是因为并没有生成嵌套关系,父组件中由于添加 scoped 注册的样式穿透是针对 date-v-father 这一前置条件下的,即:
[data-v-c61e7f05] .second-row {
  color: blue;
}
但是,目前的 DOM 结构下 data-v-c61e7f05 下面并没有 second-row 的类名。所以需要调整,父组件使用一个容器包裹起来,完成嵌套,即:
<!-- father.vue -->
<template>
  <div class="wrapper">
    <div>father</div>
    <Son />
  </div>
</template>

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

</script>

<style lang="less" scoped>
div {
  color: red;
}

:deep(.second-row) {
  color: blue;
}
</style>
去掉 scoped 样式穿透有时生效或不生效?
主要有3个原因:
1.去掉 scoped 样式就变成全局样式了
2.添加 :deep() 是把其属性选择器放在最前面
3.第三方 UI 组件只会为根元素添加 data-v-xxx 属性
案例分析
<!-- father.vue -->
<template>
  <div>
    <div>father</div>
    <Son />
  </div>
</template>

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

</script>

<style scoped>
div{
  color: red;
}
:deep(.second-row){
  color: blue;
}
</style>

<!-- son.vue -->
<template>
  <div>111</div>
  <div class="second-row">222</div>
  <div>333</div>
</template>
<script></script>

<style scoped>
.second-row{
  color: green
}
</style>
1)添加scoped,使用穿透,样式穿透生效:

2)移除scoped,使用穿透,样式穿透未生效:
由于没有了属性, :deep作用.second-row 没有属性可提,子组件定义样式生效,“穿透无效”。

3)添加scoped,不使用穿透,子组件样式生效:
父组件定义的样式带了属性,但优先级没有子组件定义的高【按照选择器的计算是优先级相同的,是因为子组件样式后加载?】

注意:这里如果子组件移除掉scoped,子组件的优先级降低,就是父组件样式生效。

4)不添加scoped,不使用穿透,子组件样式生效:
父组件定义的是全局,但优先级没有子组件定义的高。

样式穿透的一些写法
写法有:::v-deep,>>>,:deep(),/deep/,具体使用如下:
1.如果你使用的是 css,没有使用 css预处理器,则可以使用 >>>,/deep/,::v-deep。
2.如果你使用的是 less 或者 node-sass,那么可以使用 /deep/,::v-deep 都可以生效。
3.如果你使用的是 dart-sass,那么就不能使用 /deep/,而是使用 ::v-deep 才会生效。
4.但是如果你是使用 vue2.7 以上版本以及包括 vue3,::v-deep也会生效,但是会有警告
用户评论