Vue3 added: \ $attrs and \ $listeners attributes

Vue3 added: $attrs and $listeners attributes

This article mainly introduces how to use the $attrs and $listeners attributes added in Vue v2.4. It is introduced in great detail through the example code, which has certain reference and learning value for your study or work. Friends in need, please follow me to learn.

1. Preface

When multi-level component nesting needs to transfer data, the usual method is through vuex. If you only transfer data without intermediate processing, using vuex processing is a bit of a bull knife to kill chickens. Vue version 2.4 provides another method to use v-bind = "$attrs" to transfer the attributes in the parent component that are not considered props attribute binding into the child component, which is usually used together with the interitAttrs option. The reason to mention these two attributes is that the emergence of the two makes the cross component communication between components concise and business clear without relying on vuex and event bus.

First, analyze the following application scenarios:

2. Communication between component A and component B: (parent-child component)

As shown in the figure above, components A, B and C are nested in turn. According to Vue's development habits, parent-child component communication can be realized in the following ways:

  • A to B is passed to sub components by props, and B to A is realized by $emit in component B and v-on in component A
  • By setting the global Vuex sharing state, the data is obtained and updated by calculating the attribute and commit mutation, so as to achieve the purpose of parent-child component communication.
  • Vue Event Bus uses Vue instances to monitor and publish events and transfer between components.

Often, the first method can be used when the data is only communicated between parent and child components without global.

3. Communication between component A and component C: (cross multi-level component nesting relationship)

As shown in the figure above, component A and component C belong to a cross multi-level component nesting relationship. In the past, if communication between them was required, it was often realized in the following ways:

  • With the transfer of B components, props are transmitted from top to bottom, and $emit events are transmitted from bottom to top to achieve the effect of cross level component communication
  • Global state sharing with Vuex
  • Vue Event Bus uses Vue instances to monitor and publish events and transfer between components.

Obviously, the first way is through props and $emit, which makes the business logic between components bloated. Component B only acts as a transit station. If the second Vuex method is used, it seems to be overqualified in some cases (just to realize a data transfer between components, not the concept of data sharing). The use of the third case is found in the actual project operation. If it can not achieve good event monitoring and release management, it is often easy to lead to confusion of data flow. In a multi person collaborative project, it is not conducive to project maintenance.

The emergence of $attrs and $listeners solves the problem of the first case. In the process of passing props and events, component B does not have to write redundant code, but just pass $attrs and $listeners up or down.

4. Example code

  • The parent component (Father.vue) associates data to the child component. If the child component does not receive props, these data are applied to the root element of the child component as ordinary HTML features

    <template>
      <div>
        <el-table :data='list'>
          <el-table-column
            prop="name"
            label="full name"
          ></el-table-column>
          <el-table-column
            prop="study"
            label="Study subjects"
          ></el-table-column>
          <el-table-column label="operation">
            <template slot-scope="scope">
              <el-button @click='transmitClick(scope.row)'>transmit</el-button>
            </template>
          </el-table-column>
        </el-table>
        
        <!-- Son component -->
        <ChildView
          :is-show="isOpen"
          :row="row"
        >
        </ChildView>
      </div>
    </template>
    
    <script>
    import ChildView from './Child.vue'
    export default {
      components: { ChildView },
      data() {
        return {
          isOpen: false,
          row: {},
          list: [
            { name: 'Wang Li', study: 'Java' },
            { name: 'Lik', study: 'Python' }
          ]
        }
      },
      methods: {
        // Delivery event
        transmitClick(row) {
          this.isOpen = true;
          this.row = row
        }
      }
    }
    </script>
    
  • The child component (Child.vue), the middle layer, acts as the transmission intermediary between the parent component and the grandson component. Add v-bind="$attrs" to the grandson component in the child component so that the grandson component can receive data

    <template>
      <div class='child-view'>
        <p>Son component</p>
        <GrandChild v-bind="$attrs"></GrandChild>
      </div>
    </template>
    
    <script>
    import GrandChild from './GrandChild.vue'
    export default {
      // Inherit the contents of all parent components
      inheritAttrs: true,
      components: { GrandChild },
      data() {
        return {
        }
      }
    }
    </script>
    
    <style lang="stylus">
    .child-view {
      margin: 20px
      border: 2px solid red
      padding: 20px
    }
    </style>
    
    
  • Grandchild component (GrandChild.vue). In the grandchild component, props must be used to receive the data passed from the parent component

    <template>
      <div class='grand-child-view'>
        <p>Grandson assembly</p>
        <p>Data passed to grandchildren:{{row.name}} {{row.name !== undefined? 'study' : ''}} {{row.study}}</p>
      </div>
    </template>
    
    <script>
    export default {
      // You do not want to inherit the contents of all parent components, nor do you want to display attributes on the dom of the component root element
      inheritAttrs: false,
      // In this component, you need to receive the data passed from the parent component. Note that the parameter name in props cannot be changed and must be the same as that passed from the parent component
      props: {
        isShow: {
          type: Boolean,
          dedault: false
        },
        row: {
          type: Object,
          dedault: () => { }
        }
      }
    }
    </script>
    
    <style lang="stylus">
    .grand-child-view {
      border: 2px solid green
      padding: 20px
      margin: 20px
    }
    </style>
    
  • result:

    As mentioned above, if the data passed to the child component is not received by props, the data will be used as the characteristics of the child component, which are bound to the HTML root element of the component. After vue2.40, you can control whether these characteristics are displayed on the DOM element through inheritAttrs = false, such as the row and isShow passed by the parent component to the child component in the case, The subcomponent does not use props to receive, and these two data are directly used as special attributes of HTML. If the subcomponent uses inheritAttrs = true, the feature is displayed on the dom. If it is set to false, the feature is not displayed on the dom

5. Summary

$attrs

Contains attribute bindings (except class and style) in the parent scope that are not considered (and not expected) props. When a component does not declare any props, all parent scope bindings (except class and style) will be included here, and internal components can be passed in through v-bind = "$attrs" - which is very useful when creating higher-level components.

$listeners

Contains v-on event listeners in the parent scope (without. native modifiers). It can pass in internal components through v-on = "$listeners" -- very useful when creating higher-level components.

inheritAttrs

By default, attribute bindings of the parent scope that are not recognized as props will be "rolled back" and applied to the root element of the child component as ordinary HTML attributes. When composing a component that wraps a target element or another component, this may not always behave as expected. By setting inheritAttrs to false, these default behaviors will be removed. The instance attribute $attrs (also added in 2.4) can make these features take effect, and can be explicitly bound to non root elements through v-bind.

Keywords: Javascript Vue.js

Added by AndyB on Mon, 20 Sep 2021 00:56:22 +0300