1.为什么要用scoped?
当组件间的类名相同时,会发生css样式污染问题。
如果是父子组件有相同类名,则样式都为父组件中的同类名样式,如果是兄弟组件,看哪个组件最后创建,样式就以哪个为主进行覆盖之前创建的组件的样式。
//父组件中div的样式
<template>
<div class="box"></div>
<Child/>
</template>
<script>
import Child from './components/Child.vue'
export default {
components: {
Child,
}
}
</script>
<style>
.box {
background-color: red;
height: 200px;
width: 200px;
}
</style>
//子组件中div的样式
<template>
<div class="child"></div>
</template>
<style>
.child {
width: 500px;
height: 500px;
background-color: black;
}
</style>
//兄弟组件1
<template>
<div class="child"></div>
</template>
<style>
.child {
width: 500px;
height: 500px;
background-color: black;
}
</style>
//兄弟组件2(后创建)
<template>
<div class="child"></div>
</template>
<style>
.child {
width: 10px;
height: 10px;
}
</style>
从上面的图中,可以清晰的看到,父子组件只生效父组件的样式,子组件的样式不起作用了,兄弟组件后创建的样式会替换先创建的,这就是没有scoped产生的css样式污染,当给style标签加上scoped属性时就不会产生这种问题了。
2.scoped原理
当设置了scoped属性时,style里的样式仅作用于当前组件,便不会产生组件间的样式污染。
1.给组件中的样式对应的标签设置一个标签属性[data-v-hash字符串](形如:<div class="child" data-v-45972626=""></div>)
2.给css选择器的后面加上[data-v-hash字符串](形如:.child[data-v-45972626] {.....})来表示唯一性。
postcss转换
//转化前
<template>
<div class="child"></div>
</template>
<style scoped>
.child {
width:200px;
height:200px;
}
</style>
//转化后
<template>
<div class="child" data-v-45972626></div>
</template>
<style scoped>
.child[data-v-45972626] {
width:200px;
height:200px;
}
</style>
注意:
当父组件和子组件都有scoped时,父组件的data-v-hash会出现在父组件和子组件的data-v-hash后,从而再次引起样式污染(如下图,子组件再次样式失效)。
或者引入第三方组件时,需要在组件内修改样式,如果遇到同样的类名,也会再次引起样式污染。
解决这个问题有两种方法:
- 可以在<style scoped></style> 标签后再多加一个<style></style>标签用来写没有data-v-hash的样式(也就是全局通用的样式),或者直接在<style scoped></style>标签里写 :global(选择器){...}。
- 使用样式穿透(深度选择):在需要去掉data-v-hash的选择器前面加上下面的格式即可去除样式污染
- sass/less中使用'/deep/'或'::v-deep'或':deep(选择器){...}'
- 非预编译中使用'>>>'