1. 前言
本次我们主要结合一些案例研究一下vue的插槽中样式污染问题。在这篇文章中,我们主要关注以下两点:
- 父组件的样式是否会影响子组件的样式?
- 子组件的样式是否会影响父组件定义的插槽部分的样式?
2. 准备代码
2.1 父组件代码
<template>
<div class="wrap">
<div>
parent-root
</div>
<A>
<div slot-parent>
<div class="container">parent_content</div>
</div>
</A>
</div>
</template>
<script lang=ts setup>
import A from './A.vue';
</script>
<style lang=scss scoped>
.wrap{
width: 400px;
height: 400px;
background-color: lightgreen;
color: red;
}
.container{
background-color: lightcoral;
}
</style>
2.2 子组件代码
<template>
<div class="wrap" child>
<div>
child-header
</div>
<slot></slot>
<div class="container">
child-footer
</div>
</div>
</template>
<script lang=ts setup>
</script>
<style lang=scss scoped>
.container{
width: 100px;
height: 100px;
background-color: lightblue;
}
.wrap{
border: 1px solid black;
}
</style>
2.3 最终编译的代码
通过上面的代码,我们得出几个结论:
- 子组件的顶级标签会继承父组件的文件指纹。
- 子组件的插槽(父级定义的插槽代码)的顶级标签不会继承子组件的文件指纹。
3. 问题分析
3.1 父组件是否污染子组件问题
通过上面的分析,我们知道父组件的文件指纹会继承到子组件的顶级标签上,也就是说父组件的样式有可能会影响子组件的样式。
比如,我们在父组件中定义了wrap的样式:
.wrap{
width: 400px;
height: 400px;
background-color: lightgreen;
color: red;
}
那么他生成的代码如下:
此时,我们发现在父组件定义的wrap样式在子组件的顶级标签仍然起作用。此时,就会污染子组件。
目前我还没有比较好的解决方案,只能在实际开发中对于类名的命名尽量避免相同。
3.2 子组件是否会污染插槽中的样式
通过上面的分析,我们知道插槽中的html片段并不会携带子组件的文件指纹。由于在子组件定义的样式都会携带子组件的文件指纹,所以子组件定义的样式并不会影响插槽中html片段的样式。
当然,这个假定也都是在不使用v-deep
的前提下生效。如果掺杂v-deep
呢?
3.3 v-deep下子组件插槽样式的污染情况
父组件代码
<template>
<div class="wrap">
<div>
parent-root
</div>
<A>
<div slot-parent>
<div class="container">parent_content</div>
</div>
</A>
</div>
</template>
<script lang=ts setup>
import A from './A.vue';
</script>
<style lang=scss scoped>
.wrap{
width: 400px;
height: 400px;
background-color: lightgreen;
color: red;
}
.container{
background-color: lightcoral;
}
</style>
子组件代码
<template>
<div class="wrap" child>
<div>
child-header
</div>
<slot></slot>
<div class="container">
child-footer
</div>
</div>
</template>
<script lang=ts setup>
</script>
<style lang=scss scoped>
.container{
width: 100px;
height: 100px;
background-color: lightblue;
}
.wrap{
border: 1px solid black;
::v-deep .container{
border: 5px dashed lightsalmon;
}
}
</style>
通过上面分析的代码,我们发现parent_content所在的标签和child-footer所在的标签都生效了,也就是说子组件的样式污染了父组件的标签。
这一点在开发中要注意,要慎重使用v-deep
,不然会产生难以预料的结果。这里有可能有人会想,在最后加一个v-deep
就行了呗。
而实际事与愿违,因为对于多个v-deep
,vue只能识别出第一个,后面的会按照样式名为v-deep
进行渲染。
.wrap{
border: 1px solid black;
::v-deep .container::v-deep{
border: 5px dashed lightsalmon;
}
}