组件封装之v-bind="attrs"和v−on="listeners"

377 阅读2分钟

一、需求场景描述

在封装组件时经常用到的就是 props和emit,但是当我们嵌套组件实在太多时候,一遍遍的使用这个组合就太繁琐了,而eventBus也被本人嫌弃(找代码太耗费时间),再就是vuex,但是vuex的体量又太大,为了封装组件传递几个属性,没太大必要。

v-bind="attrs"和v−on="listeners"就给我们带来惊喜。它们可以使得封装后的组件, “继承”原组件的几乎所有(class和style以及.native的事件除外) v-bind 属性和 v-on 事件,且用法和作用与在原组件一样。同样适用于封装第三方组件。

例如有个爷爷组件grandpa.vue,并且层级关系如下

<grandpa>
  <father>
   <son>
   </son>
  </father>
</grandpa>

然后在grandpa组件层级上传入一堆属性和方法

<template>
  <div>
  <grandpa name="tlb" :flag="true" :numbe="2" @isOpen="isOpen" @setData="setData"></grandpa>
  </div>
</template>
<script>
import grandpa from ./grandpa.vue
  export default {
    components:{grandpa}
    name: 'index',
    data() {
      return {
      }
    },
    methods:{
        isOpen(){
          console.log(1)
        },
        setData(){
          console.log(2)
        }
    }
  }
</script>
<style  scoped>
</style>

在father.vue 中只接收了name和flag属性,其他未接收的属性将作为下一级传递的参数,

<template>
  <section>
    <div class="mt-10">
      <son v-bind="$attrs" v-on="$listeners" />
    </div>
  </section>
</template>

<script>
  import son from './son';
  export default {
    components: {
      son
    },
    props: {
      name: {
        type: String,
        default: 'default'
      },
      flag: {
        type: Boolean,
        default: 'false'
      }
    }
  };
</script>

在son.vue组件中我们只接收了number属性,但是这个属性是其父组件father使用 v-bind="$attrs" 从grandpa组件接收到的,father组件本身并没有使用props接收这个属性,但是son组件中的属性确可是使用

<template>
  <section>
    <div>
      <!--在$attrs里面只会有props没有注册的属性
      number属性已经被接收,此处不会显示值-->
      {{ $attrs['number'] }}  
      <br>
      {{ number }}
    </div>
  </section>
</template>

<script>
  export default {
    props: {
      number: {
        type: number,
        default: 0
      }
    },
    mounted() {
      console.log(this.$attrs);
      console.log(this.$listeners);
      this.$emit('isOpen');
      this.$emit('setData');
      //等同于下面两个
      //this.$listeners.isOpen();
      //this.$listeners.setData();
    }
  };
</script>

使用v-bind="$attrs"时 inheritAttrs属性的用法

export default {
  name: 'Son',
  inheritAttrs: false, //可选值 true/false
  props: {
    age: {
      type: Number,
      default: 0,
    }
  }
}
  1. 下图是设置inheritAttrs: false时候dom树上的展示 image.png

  2. 下图是设置inheritAttrs: true时候dom树上的展示 image.png

关键

  1. v-on="将父组件标签上的自定义事件向下传递其子组件可以直接通过emit(eventName)的方式调用。 vm.listeners: 包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。
  2. 是vm.$attrs :包含了父作用域中props对象为接收的属性(class 和 style 除外)。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class 和 style 除外),
  3. vm.$attrs的用法中,假若三层结构中,2级和3级用props对象去接收同一个值,那么第三级将访问不到这个属性,除非在2级中单独对这个属性传递到3级
  4. v-bind="$attrs": 将调用组件时的组件标签上绑定的非props的特性(class和style除外)向下传递。在子组件中应当添加inheritAttrs: false(可避免避免绑定属性出现在在子组件的根元素上)。
  5. this.attrsthis.attrs和this.listeners 获取到的值都是json的形式,