Understanding Vue attribute fallthrough ($attrs, $listeners)

A Fallthrough attribute ("$attrs”) is an attribute that falls into the component element from what is declared on a component. The attributes that declare in props emit and specifically class and style will exclude from the fallthrough attributes (v-bind="$attrs”). Just like the attributes, the "$listeners" is also the same but instead, it binds the event listener.

Single root element attributes (v-bind="$attrs")

The attributes will automatically bind to the root element if the component has a single root element.

In the <BaseInput /> component

<template>
  <input type="text" />
</template>

A parent using this component

<BaseInput test="a" />

The render DOM 

<input type="text" test="a">

Nested element fallthrough attributes (v-bind="$attrs")

We can manually bind the attributes fallthrough with v-bind="$attrs" to the specific element or a deeper element in case we don’t want it on the single root element. In Vue 2 the class and style binding will always bind to the top root element unless passed it by props. The “inheritAttrs: false” will prevent the attributes bind to the single root element.

In the <BaseInput /> component

<template>
  <div>
    <label>Label</label>
    <input v-bind="$attrs" type="text" />
  </div>
</template>

<script>
export default {
  inheritAttrs: false,
};
</script>

A parent using this component

<BaseInput test="abc" />

The render DOM 

<div>
  <label>Label</label>
  <input type="text" test="abc">
</div>

Event binding with $listeners

Just like the v-bind="$attrs", v-on="$listeners" will bind the event from its parent component into a specific inner element. 

In the <BaseInput /> component

<template>
  <div>
    <label>Label</label>
    <input v-on="$listeners" type="text" />
  </div>
</template>

A parent using this component

<template>
  <div>
    <BaseInput @click="test" />
  </div>
</template>

<script>
import BaseInput from '@/components/BaseInput.vue';

export default {
  name: 'HelloWorld',
  components: {
    BaseInput,
  },
  methods: {
    test() {
      console.log('test');
    },
  },
};
</script>

Here the event click from the component will bind to the input element. Only when users click on the input element that the method is fired.