vue中父组件改变子组件样式的6种方法

10,197 阅读2分钟

在vue实际开发的项目中。 经常会遇到封装的组件需要改变样式的情况。 这时候, 如果改变样式的方式不当就会对封装的组件造成污染。 如果组件内部对基本样式的封装不够合理。 又容易造成调用组件时过多的处理样式问题。 这些都是不合理的。 本文讨论父组件修改子组件的样式, 可以在有不同的样式需求的时候根据选择游刃有余的去处理。 这里以公共的title组件为例解释。

1. 修改class

可以直接传递class名给子组件。 或者根据布尔的true或false来显示隐藏class名。

2. 修改style

可以根据对象键值对去修改style样式。 由于style的优先级较高。 所以往往可以更改样式。 在子组件中定义好键名, 父组件中传入键值就可以更改子组件的属性了。 也可以直接在父组件中写style对象。 传入子组件, 这样就不必再在子组件中提前写好键值, 方便对一些不常用的属性的修改(注意: 由于对象的特性, 如果对象名相同。 后面的对象会覆盖前面的对象, 所以尽量将customStyle放入后面)。

3. 使用computed来获取计算后的属性

可以根据props传入的参数, 来获取计算属性计算后的样式。

4. 使用穿透

由于vue的scoped具有单页面样式保护特性。 所以可以使用穿透 ::v-deep 去修改子组件中的样式 语法: 父组件class :: v-deep 子组件class

5. 父组件使用特殊style写法(重要, 这样子组件中js和css都可以处理父组件传入的变量)。

原理: 父组件通过style方式传入子组件变量。 子组件根据变量改变颜色。

// 父组件
style="--lc: pink"
// 子组件
$dfColor: lightblue;
background: var(--lc, $dfColor); // 第二个参数为默认变量值

6. 利用javascript去改变子组件的样式

需要在this.$nextTick之后才能获取到子组件的dom。 然后操作dom去修改子组件的样式。 也可以先在子组件中定义好函数, 然后通过父组件调用子组件的函数去改变子组件的样式。

created() {
    this.$nextTick(() => {
      let ele = this.$refs.titleRef;
      let pp = ele.$el.getElementsByTagName('p')[0];
      pp.style.background = "red"
    })
},

7. 看代码

  1. 父组件
// 父组件
<template>
  <div>
    <g-title
      t="测试哈"
      class="aa"
      ref="titleRef"
      cc="cc"
      borderBottom
      style="--lc: blue;"
      :customStyle="myStyle"
    ></g-title>
  </div>
</template>

<script>

export default {
  name: "Test1",
  data() {
    return {
      myStyle: {
        fontSize: '100px',
      }
    };
  },
  created() {
    // 这里必须等dom更新完毕才可以操作dom
    this.$nextTick(() => {
      let ele = this.$refs.titleRef;
      let pp = ele.$el.getElementsByTagName('p')[0];
      pp.style.background = "red"
    })
  },
};
</script>
  1. 子组件
<!--
* @描述
* 公共title组件。可以设置title,  可以设置左侧竖条颜色
* @使用方法
* <g-title t="4. 回到顶部"></g-title>  <g-title t="左侧粉色" style="--lc: pink"/>
* 
<g-title
  t="测试哈"
  class="aa"
  ref="titleRef"
  cc="cc"
  borderBottom
  style="--lc: pink;"
  :customStyle="myStyle"
></g-title>
* @LastEditTime: 最后更新时间
* 2021-10-30
-->
<template>
  <div
    v-if="title || t"
    class="bb"
  >
    <p
      class="title"
      :class="{'has-border':borderBottom, cc}"
      :style="[{color: color}, customStyle, setBorderRadius]"
      data-color="blue"
    >{{title || t}}</p>
  </div>
</template>
    
<script>
let cl = "yellow";
export default {
  name: "GTitle",
  props: {
    title: {
      type: String,
      default: ''
    },
    color: {
      type: String,
      default: '',
    },
    t: {
      type: String,
      default: ''
    },
    cc: {
      type: String,
      default: ''
    },
    other: {

    },
    customStyle: {
      type: Object,
      default: () => { },
    },
    borderBottom: {
      type: Boolean,
      default: false,
    },
    br: {
      type: String,
      default: '20',
    }
  },
  computed: {
    setBorderRadius() {
      let obj = {};
      // 这里可以根据var(--lc)得到父组件传入变量的值。 如果--lc没值, 就取第二个参数的值
      if (this.br) {
        obj = {
          borderRadius: this.br + 'px',
          borderTop: this.br + 'px solid var(--lc, yellow)'
        }
      }
      return obj;
    }
  },
};
</script>
<style scoped lang='scss'>
$dfColor: lightblue;
.title {
  width: 100%;
  height: 28px;
  padding-left: 22px;
  line-height: 28px;
  position: relative;
  margin: 20px 0;
  font-weight: 600;
  box-sizing: border-box;
  color: var(--cl, $dfColor); // 如果不传--cl。 默认使用第二个参数的变量
}
.title::before {
  position: absolute;
  top: 3px;
  bottom: 0;
  left: 0;
  content: "";
  width: 6px;
  height: 22px;
  z-index: 1;
  background-color: var(--lc, $dfColor); // 左侧的竖条颜色
}
.has-border {
  border-bottom: 1px solid var(--lc, $dfColor);
}
.cc {
  margin: 100px;
}
</style>