一、什么是scoped,为什么要用
在vue文件中的style标签上,有一个特殊的属性:scoped。
当一个style标签拥有scoped属性时,它的CSS样式就只能作用于当前的组件,通过该属性,可以使得组件之间的样式不互相污染。
二、scoped的原理
1、为组件实例生成一个唯一标识,给组件中的每个标签对应的dom元素添加一个标签属性,data-v-xxxx
2、给<style scoped>
中的每个选择器的最后一个元素添加一个属性选择器,原选择器[data-v-xxxx]
,如:原选择器为.container #id div
,则更改后选择器为.container #id div[data-v-xxxx]
注意:是加在最后一个元素上的!
//.a为大div父组件 .b为子组件根元素 .c为子组件内的子元素.a .b .c{//多层color:red}//会渲染成.a .b .c[data-v-2311c06a]{color:red}
三、示例
转译前的vue代码
<template><div class="example">hello world</div>
</template>
<style scoped>
.example {color: red;
}
</style>
<template><div class="example" data-v-49729759>hello world</div>
</template>
<style scoped>
.example[data-v-49729759] {color: red;
}
</style>
二: /deep/深度作用选择器
1.说明
通过上面的讲解,我们了解到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>
其实/deep/ 编译后的结果将会是一个属性选择器
2. 案例
通过上面的讲解,那他究竟有什么作用呢?下面我们通过一个案例体会他的真正作用。
再vue的世界中,我们很多时候会使用第三方库,比如element-ui
等。如果我想该里面的样式,之前好多同事会把样式放到全局中,这样显而易见容易污染其他的元素,此时,/deep/
就会大显身手。
我们暂时选择ant-design库作为测试。
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{/deep/ .ant-checkbox-inner{background-color: red;}}
}
</style>
再看样式表,发现给check加了文件指纹,check是我们vue组件的标签,因此编译的时候会加上文件指纹。这样就完美解决了问题。