如何在vue的父组件中修改子组件的样式

6,879 阅读4分钟

目录

场景需求:

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之类的开源公共组件,可以使用方案四的方式实现。