Skip to content

Vue attrs listeners 透传

MyButton.vue

vue
<script>
export default {
  name: 'MyButton',
  props: {
    size: {
      type: String,
      default: 'medium',
    },
    type: {
      type: String,
      default: 'primary',
    },
    disabled: Boolean,
  },
  methods: {
    onClick() {
      this.$emit('click')
    },
  },
}
</script>

<template>
  <el-button
    :size="size"
    :type="type"
    :disabled="disabled"
    @click="onClick"
  >
    <slot />
  </el-button>
</template>
<script>
export default {
  name: 'MyButton',
  props: {
    size: {
      type: String,
      default: 'medium',
    },
    type: {
      type: String,
      default: 'primary',
    },
    disabled: Boolean,
  },
  methods: {
    onClick() {
      this.$emit('click')
    },
  },
}
</script>

<template>
  <el-button
    :size="size"
    :type="type"
    :disabled="disabled"
    @click="onClick"
  >
    <slot />
  </el-button>
</template>

UseMyButton.vue

vue
<template>
  <my-button
    type="success"
    disabled
    round
    native-type="submit"
  >
    测试
  </my-button>
</template>
<template>
  <my-button
    type="success"
    disabled
    round
    native-type="submit"
  >
    测试
  </my-button>
</template>

如何理解inheritAttrs(默认值为true)这个选项呢?我们知道,一个组件如果要接受父组件传来的属性,是需要先在props里面预定义好的。比如前面的例子,我在MyButton预定义了3个属性,分别是size,type,disabled,意思是MyButton这里只接受3个prop

我们应该是希望roundnative-type="submit"能够传到el-button,产生应有的效果。然而,roundnative-type="submit"仅仅是挂在了根元素的attribute上,并没有真正起到应有的作用!

也就是说,inheritAttrs的作用是:使那些没有在props中定义的属性,直接以attribute的形式作用在组件的根元素上!

首先,不能让那些未被props标识的属性直接落到根元素上,所以需要设置inheritAttrsfalse

然后,要获取到那些未被props标识的属性,并直接绑定到el-button。恰好,Vue提供了$attrs[2]用于获取这些属性,而v-bind本身就能绑定一个对象,这是容易被我们忽略的!

处理完属性透传,接下来我们还要处理事件,类似于$attrs$listeners也能把父组件中对子组件的事件监听全部拿到,这样我们就能用一个v-on把这些来自于父组件的事件监听传递到下一级组件。

vue
<script>
export default {
  name: 'MyButton',
  inheritAttrs: false,
  props: {
    size: {
      type: String,
      default: 'medium',
    },
  },
  computed: {
    customizedAttrs() {
      return {
        size: 'medium',
        // 支持传过来的size覆盖默认的size
        ...this.$attrs,
      }
    },
  },
}
</script>

<template>
  <el-button
    v-bind="customizedAttrs"
    v-on="$listeners"
  >
    <slot />
  </el-button>
</template>
<script>
export default {
  name: 'MyButton',
  inheritAttrs: false,
  props: {
    size: {
      type: String,
      default: 'medium',
    },
  },
  computed: {
    customizedAttrs() {
      return {
        size: 'medium',
        // 支持传过来的size覆盖默认的size
        ...this.$attrs,
      }
    },
  },
}
</script>

<template>
  <el-button
    v-bind="customizedAttrs"
    v-on="$listeners"
  >
    <slot />
  </el-button>
</template>

Vue 这个透传技巧,治好了我的重度代码洁癖(收藏!)