先说环境吧:vue 3.2.45,也试过vue 3.2.16
vue的scoped样式隔离
原理:
在当前组件的.vue文件中,如果style标签加了scoped属性,那么在组件渲染为DOM时,会对每个组件中的DOM元素添加格式为:data-v-[hash:8]的属性,然后该组件的所有`选择器`也会添加上对应的`[data-v-[hash:8]]`属性选择器来只对自身组件产生影响。以此来实现样式隔离。例子如下:
父组件:App.vue
<script>
import Comp from './Comp.vue';
export default {
components:{
Comp
},
data() {
return {
msg: 'Hello World!'
}
}
}
</script>
<template>
<p>{{ msg }}
</p>
<Comp/>
</template>
<style scoped>
p{
color:red;
}
</style>
子组件:Comp.vue
<script>
export default {
data() {
return {
}
}
}
</script>
<template>
<div>
<p>
In Comp
</p>
</div>
<p>
In Comp
</p>
</template>
<style scoped>
p{
font-size:20px;
color:blue;
}
</style>
DOM结果
效果图
可以看到
- 子组件Comp多了data-v-37b1dc3d属性,父组件App多了data-v-472cff63,并且style也设置了属性选择器
- 由于属性选择器的缘故,实现了样式隔离。
小细节:
1. 如果父组件App.vue不设置scoped会怎么样
假设App.vue的style和Comp.vue的style如下
<!--App.vue -->
<style>
p{
color:red;
}
</style>
<!-- Comp.vue -->
<style scoped>
p{
font-size:20px;
}
</style>
父组件的样式就成了全局的样式了。
2. 如果父组件App.vue和子组件Comp.vue都不设置scoped会怎么样
看结果就知道,俩组件的样式都成了全局的了
3. 注意样式继承
在1的基础上,首先父组件和子组件的样式都设置上scoped属性。然后父组件App.vue的template如下
<template>
<p>{{ msg }}
<Comp/>
</p>
<Comp/>
</template>
结果就是
即使是样式隔离也要注意 样式继承
样式穿透的细节
1. 子组件的单节点和多节点
代码如下
<!--App.vue -->
<script>
import Comp from './Comp.vue';
export default {
components:{
Comp
},
data() {
return {
msg: 'Hello World!'
}
}
}
</script>
<template>
<p>{{ msg }}
</p>
<Comp/>
</template>
<style scoped>
p{
color:red;
}
</style>
<!--Comp.vue -->
<script>
export default {
data() {
return {
}
}
}
</script>
<template>
<p>
In Comp
</p>
<p>
In Comp
</p>
</template>
<style scoped>
p{
font-size:20px;
}
</style>
这里Comp.vue的template包含了俩p标签,结果如下
能看出来,正常的实现样式隔离,不同组件各加各的data-v-[hash]属性
但是!
如果子组件只有一个p标签,其他条件不变时:
<template>
<p>
In Comp
</p>
</template>
结果却是:
这就很有意思了。
如果说把Comp.vue的template设置如下:
<template>
<div>
<p>
In Comp
</p>
</div>
</template>
结果则是
更进一步的,如果说把Comp.vue的template设置如下
<template>
<div>
<p>
In Comp
</p>
</div>
<div>
<p>
In Comp
</p>
</div>
</template>
总结
这么多次对比试验能看出来,当子节点为单节点时,由于template标签并不会出现在DOM树中,所以那个单节点就会同时具有父组件和子组件的data-v-[hash]属性。如果是多节点,则只会有组件自身的data-v-[hash]属性。有必要去看看vue源码了。
要说这个细节有啥用嘛。。。emmmmmm。。。就当注意一下比较好。假如父组件想自己的p标签字体是红色,子组件为默认颜色。那如果子组件不套一个div或者是一个单独的p标签,就会被父组件的样式所影响。以此类推到别的标签也是同理
2. 样式穿透
不想截图了。。。
样式穿透所使用的/deep/也好,>>> 也好。最后会被渲染为父组件的data-v-[hash]。也就是说,假设父组件的style为:>>> p{color:blue},想用来改变子组件的p标签字体颜色来实现样式穿透。那么必须要保证子组件的template只有一个子节点,即上述的单个根节点情况。因为,单根节点(假设为div)会同时具有父组件和子组件的data-v-[hash]。
又因为/deep/也好,>>> 也好。最后会被vue更改为父组件的data-v-[hash](假设为data-v-123456)。此时就能通过[data-v-123456] p{color:blue;}改变子组件p标签的字体颜色。而由于父组件其他的DOM,如某一个p标签,其选择器为p[data-v-123456]{......},故而不会受到影响。