目录
场景需求:
1、业务开发过程中多组件引入同一个子组件
2、每个父组件中对子组件的样式要求都不同
3、解决这种问题的方式
a、父组件中添加类名,通过css后代选择器更改子组件样式
b、父组件引用时,prop一个值,用以区分不同父组件的调用
c、父组件中多style样式
d、使用deep深度选择器修改
1、业务场景复现
现在有两个组件分别叫做father1,father2,分别引入了elementUi的button组件,但是在这两个父级组件中我们都需要对这个button的样式进行微调,如果我们改变公共的子组件的样式,那么这样会同步影响到两个父组件中,该子组件的展示。
话不多说,上代码:
father1.vue:
<template>
<div>
<p>按钮1</p>
<el-button type="success" plain @click="open">成功按钮</el-button>
<com-dialog ref="comdialog" class="comdia"></com-dialog>
</div>
</template>
...
<style>
</style>
father2.vue:
<template>
<div>
<p>按钮2</p>
<el-button type="success" plain @click="open">成功按钮</el-button>
<com-dialog ref="comdialog" class="comdia"></com-dialog>
</div>
</template>
...
<style>
</style>
dialog.vue:
<template>
<div>
<div class='acXdialog' id=''>
<x-dialog v-model="showHideOnBlur" class="dialog-demo" hide-on-blur dialog-transition="false">
<div class="img-box clearfix">
<div class="xdialogImg" :style="{'backgroundImage':'url()'}"></div>
<div class="title">title</div>
<div class="desc">wordword</div>
<div class="iknowBtn" @click="hide">按钮文本</div>
</div>
<div @click="showHideOnBlur=false">
<span class="vux-close"></span>
</div>
</x-dialog>
</div>
</div>
</template>
...
<style>
</style>
本着代码及简原则,用最简单的代码复现业务场景,在这里我们可以看到,father1和father2都引入了一个子组件dialog,现在我们需求在这两个父组件中,修改这个dialog子组件的样式,那么我们该如何操作呢。
这是按钮的展示,点击每个按钮都会有对应的dialog弹框:
按钮点击之后的效果:
2、解决这种业务需求的几种方式
1)、第一种方案:在父组件中添加类名,通过css后代选择器更改子组件样式
代码如下:
father1.vue:
<template>
<div>
<p>按钮1</p>
<el-button type="success" plain @click="open">成功按钮</el-button>
<com-dialog ref="comdialog" class="comdia"></com-dialog>
</div>
</template>
...
<style>
.comdia .acXdialog .img-box .title {//修改子组件的title
color: red !important;
}
</style>
father2.vue文件不做任何变动这样修改之后页面会出现什么样的变化呢:
通过截图我们发现我们在father1中对dialog组件样式修改,造成了全局的污染,无论是点击成功按钮1和成功按钮2title的颜色都发生了变化,这是因为我们并没有加scoped,但是如果我们在style里面加上scoped,那么又会是什么效果呢
father1.vue:
<template>
<div>
<p>按钮1</p>
<el-button type="success" plain @click="open">成功按钮</el-button>
<com-dialog ref="comdialog" class="comdia"></com-dialog>
</div>
</template>
...
<style scoped>
.comdia .acXdialog .img-box .title {//修改子组件的title
color: red !important;
}
</style>
首先我们看下编译之后的代码:
father1.vue:
.comdia .acXdialog .img-box .title[data-v-3e321e06] {
color: red !important;
}
dialog.vue:
[data-v-7be471ee] .acXdialog .img-box .title {
height: 0.583333rem;
color: #252525;
margin-top: 0;
font-size: 0.425rem;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
padding-left: 0.333333rem;
padding-right: 0.333333rem;
}
在这里我们可以看到,添加了scoped属性之后,每个vue组件对应的样式代码都会生成一个自定义属性data-v-${hash_8},这些值都是唯一的,保证了组件样式有且只能作用于当前的vue组件中,所以当我们添加scoped属性之后,我们是无法在父组件修改子组件的样式,最后我们再来看下最后编译的效果图:
通过最后的效果图我们也能得知在组件中添加scoped属性的话,是无法修改当前组件之外的组件样式;
第一种方法可以改变子组件样式,但是会存在全局样式污染的情况。
2)、第二种方案:父组件引用时,prop一个值,用以区分不同父组件的调用
话不多说,先上代码
father1.vue:
<template>
<div>
<p>按钮1</p>
<el-button type="success" plain @click="open">成功按钮</el-button>
<com-dialog ref="comdialog" class="comdia" color="red"></com-dialog>
</div>
</template>
...
father2.vue:
<template>
<div>
<p>按钮2</p>
<el-button type="success" plain @click="open">成功按钮</el-button>
<com-dialog ref="comdialog" class="comdia" color="green"></com-dialog>
</div>
</template>
...
dialog.vue:
<template>
...
<div class="title" :style="{color:color}">title</div>
...
</template>
通过这种方式也可以修改子组件的样式,并且能根据不同的父组件来进行任意配置,但是这种方式有一个弊端,对公共组件的侵入量比较大,如果对子组件多个样式进行更改,那么整个子组件很多地方都需要更改。
同样,我们也把最后的结果图片贴出来:
3)、第三种方案:父组件中多style样式
<style>
.comdia .acXdialog .img-box .title {//修改子组件的title
color: red !important;
}
</style>
<style scoped>
/* 本地样式 */
</style>
其实这种方案和方案一的原理是一样的,但是这种写法多了一个style,用以区分全局和局部的,这样操作依然会存在对全局样式污染的情况。
4)、第四种方案:使用deep深度选择器修改
首先我们需要在父组件代码中做一些修改,我们在father1.vue组件中依然加上scoped,我们可以尝试看看编译之后的效果
代码如下:
father1.vue:
<template>
<div>
<p>按钮1</p>
<el-button type="success" plain @click="open">成功按钮</el-button>
<com-dialog ref="comdialog" class="comdia"></com-dialog>
</div>
</template>
...
<style scoped>
/deep/.comdia .acXdialog .img-box .title {
color: red !important;
}
</style>
father2.vue:
<template>
<div>
<p>按钮2</p>
<el-button type="success" plain @click="open">成功按钮</el-button>
<com-dialog ref="comdialog" class="comdia"></com-dialog>
</div>
</template>
...
<style scoped>
</style>
我们的子组件不做任何变化,虽然我们在父组件中添加了scoped,但是依然可以穿透当前的父组件修改对应子组件的样式,首先我们来看下编译之后的代码
father1.vue:
[data-v-3e321e06].comdia .acXdialog .img-box .title {
color: red !important;
}
到这一步我们可以发现,使用deep之后,我们会在当前的组件中从新生成一个新的data-v-${hash_8},而这个自定义的属性值挂载在后代选择器的最高节点上,和方案一挂载在title上是不一样的,所以可以在父组件修改子组件的样式。
最后上图:
总结:
通过上面四种方案其实我们都可以做到对子组件的样式修改,如果是自己写的公共组件,可以使用方案三的方式,自己做好定义,如果使用类似elementUi之类的开源公共组件,可以使用方案四的方式实现。