一文弄懂vue中样式穿透v-deep

发布时间:2024年01月06日

1. 前言

vue3的世界里,有一个style标签的关键字v-deep,官网称之为样式穿透。那他究竟是什么原理呢?又是怎么工作的呢?让我们一起探究一下。

2. 准备工作

需要实现搭建一个vue3的环境,我这里使用的是vite+vue3的架构进行研究。不会的可以自行上网了解一下。

3. scoped的作用

在了解v-deep之前,我们有必要先了解一下style标签中scoped的作用。

<template>
  <div></div>
</template>
<script lang=ts setup>
</script>
<style lang=scss scoped>
</style>

我们知道,scoped具有作用域的意思,也就是说如果在style标签中加上scope标签,那么这里面书写的样式不会再其他vue文件中生效。那他的原理又是什么呢?

3.1 style生效的位置

我们知道,所有的vue文件中的style标签的内容,最终会渲染到html文件的head中的style标签中。
如下vue代码

<template>
  <div class="container">
    <span>1111</span>
  </div>
</template>
<script lang=ts setup>
</script>
<style lang=scss>
.container{
    background-color: lightblue;
    width: 100px;
    height: 100px;
}
</style>

那么我们打开网页,查看源码
在这里插入图片描述
再style标签中,会出现一个属性data-vite-dev-id,他指向的是当前样式所对应的文件路径以及属性信息。

3.2 没有scoped效果

在这里插入图片描述
基本和源代码的格式一样。

3.3 增加scoped效果

此时,我们发现相较于源码,再生成的文件的类选择器.container中,多了一个[data-v-68595a3e]属性选择器,这又是做什么的呢?其实,他是每个vue文件的文件指纹。如下图,再每个vue文件template标签的html片段中每个标签,都会生成一个文件指纹。我们发现container的容器中又两个文件指纹,另外一个是继承了父组件的文件指纹。
在这里插入图片描述
这也正是scope的基本原理。再style中,选择器定义了选择器.container[data-v-68595a3e],我们知道这是并且的关系,满足类名.container并且包含属性data-v-68595a3e的,由于文件指纹具有唯一性,所以<style lang=scss scoped>中的样式只针对当前文件生效。

4. v-deep效果

4.1 v-deep原理

通过上面的讲解,我们了解到scope主要通过文件指纹控制作用域范围。而针对每个样式的定义,只能出现一个文件指纹。我们看如下代码:

<template>
  <div class="container">
    <span>1111</span>
  </div>
</template>
<script lang=ts setup>
</script>
<style lang=scss scoped>
.container{
    background-color: lightblue;
    width: 100px;
    height: 100px;
    .abc{
        background-color: #fff;
        .def{
            color: red;
        }
    }
}
</style>

这里我们使用的是scss语法,我们看一下编译后的呈现形式:
在这里插入图片描述
通过截图我们发现会在每个样式后面都加上一个文件指纹的属性。
那我现在有个需求,我想把文件指纹加到中间该怎么办呢?此时,v-deep就派上用途了。看下面代码:

<template>
  <div class="container">
    <span>1111</span>
  </div>
</template>
<script lang=ts setup>
</script>
<style lang=scss scoped>
.container{
    background-color: lightblue;
    width: 100px;
    height: 100px;
    ::v-deep.abc{
        background-color: #fff;
        .def{
            color: red;
        }
    }
}
</style>

在这里插入图片描述
我们发现,针对后面两个样式,文件指纹追加到中间了。针对中间的样式,做一些解释,我们知道,scss语法,会对嵌套的样式进行转换。

.container{
	.abc{
	}
}

上面的代码转换称为.container .abc,如果我们在.abc前面加上::v-deep,相当于.container ::v-deep .abc
哎?我们似乎明白点什么了?v-deep不就是文件指纹吗?
所以我们得出一个结论:

加上scoped标签,就相当于在样式的末尾追加了一个v-deep

<style lang=scss scoped>
.container::v-deep{
    background-color: lightblue;
    width: 100px;
    height: 100px;
}
</style>

在这里插入图片描述
既然v-deep可以追加文件指纹,那么style如果不加scope,但是样式末尾追加v-deep会生效吗?

<style lang=scss>
.container::v-deep{
    background-color: lightblue;
    width: 100px;
    height: 100px;
}
</style>

在这里插入图片描述
很遗憾,他就不认识v-deep是什么意思了。所以这里有得出一个结论

v-deep只有在scoped下才能生效。

4.2 v-deep有效性

那么他都对哪些选择器有效。

4.2.1 &子选择器

在scss中,&相当于父元素。

<style lang=scss scoped>
.container{
    background-color: lightblue;
    width: 100px;
    height: 100px;
    &::v-deep .abc{
        background-color: #fff;
        .def{
            color: red;
        }
    }
}
</style>

在这里插入图片描述

4.2.2 伪类和伪元素选择器

放到伪类后面

<style lang=scss scoped>
.container{
    background-color: lightblue;
    width: 100px;
    height: 110px;
    &.abc{
        background-color: #fff;
        .def:hover::v-deep::after{
            color: red;
        }
    }
}
</style>

在这里插入图片描述
放到伪元素前面

<style lang=scss scoped>
.container{
    background-color: lightblue;
    width: 100px;
    height: 110px;
    &.abc{
        background-color: #fff;
        .def:hover::after::v-deep{
            color: red;
        }
    }
}
</style>

在这里插入图片描述
伪元素后面再加上一个标签

<style lang=scss scoped>
.container{
    background-color: lightblue;
    width: 100px;
    height: 110px;
    &.abc{
        background-color: #fff;
        .def:hover::after div::v-deep{
            color: red;
        }
    }
}
</style>

在这里插入图片描述
通过上面三个示例,我们发现v-deep会越过伪类和伪元素选择器。

4.2.3 多个v-deep

<style lang=scss scoped>
.container{
    background-color: lightblue;
    width: 100px;
    height: 110px;
    ::v-deep.abc{
        background-color: #fff;
        .def:hover::after div::v-deep{
            color: red;
        }
    }
}
</style>

在这里插入图片描述
第一个出现v-deep会生效,后面再出现的按照样式处理。

5 案例

通过上面的讲解,那他究竟有什么作用呢?下面我们通过一个案例体会他的真正作用。
再vue的世界中,我们很多时候会使用第三方库,比如element-ui等。如果我想该里面的样式,之前好多同事会把样式放到全局中,这样显而易见容易污染其他的元素,此时,v-deep就会大显身手。

5.1 准备代码

我们暂时选择ant-design库作为测试。

  • 安装ant-design
     yarn add ant-design-vue@next 
    
  • main.ts配置主要代码
    import Antd from 'ant-design-vue'
    import 'ant-design-vue/dist/reset.css'
    app.use(router).use(Antd).mount('#app')
    

index.vue

<template>
  <div class="container">
    <a-checkbox class="check" v-model:checked="checked">Checkbox</a-checkbox>
  </div>
</template>

<script lang=ts setup>
import { ref } from 'vue';
const checked = ref(true)
</script>

<style lang=scss scoped>
.container{
    background-color: lightblue;
    width: 500px;
    height: 510px;
}
</style>

此时,我们看一下生成的代码片段:
在这里插入图片描述
在这里插入图片描述

假如我现在有个需求,想把复选框中间的蓝色变成红色,很容易想到需求修改ant-checkbox-inner的样式,如下代码:

<style lang=scss scoped>
.container{
    background-color: lightblue;
    width: 500px;
    height: 510px;
    .check{
      .ant-checkbox-inner{
        background-color: red;
      }
    }
}
</style>

然而,并没有生效,我们看一下生成的代码:
在这里插入图片描述
我们发现在ant-checkbox-inner后面加了一个文件指纹。而实际这是第三方库(也就是已经打包好的文件),里面的标签根本不会出现文件指纹。这样就导致我们写的样式文件不能作用于ant-checkbox-inner上。此时,我们很容易想到要把样式变成全局。

<style lang=scss>
.container{
    background-color: lightblue;
    width: 500px;
    height: 510px;
    .check{
      .ant-checkbox-inner{
        background-color: red;
      }
    }
}
</style>

在这里插入图片描述
虽然样式生效了,但是出现了最严重的问题,它会污染到其他vue组件使用的checkbox组件。此时v-deep就登场了。代码如下:

<style lang=scss scoped>
.container{
    background-color: lightblue;
    width: 500px;
    height: 510px;
    .check{
      ::v-deep .ant-checkbox-inner{
        background-color: red;
      }
    }
}
</style>

在这里插入图片描述
此时我们发现样式生效了。
再看样式表,发现给check加了文件指纹,check是我们vue组件的标签,因此编译的时候会加上文件指纹。这样就完美解决了问题。

文章来源:https://blog.csdn.net/oYinHeZhiGuang/article/details/135420064
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。