preface

When encapsulating third-party components, we often encounter the problem of how to use Attributes, Events, Methods, and Slots of third-party components from encapsulated components.

Of course, this problem is not difficult to solve, with ordinary methods to solve it inevitably into tedious and repetitive work, and packaged component code is not high readability.

This column will introduce three techniques for using Attributes, Events, and Slots from third-party components. The techniques for using Methods from third-party components need to be optimized.

Use properties of third-party components

The el-Input input box component that encapsulates an elementUI is called myInput. How do you disable the input box by adding a disabled attribute to the myInput component? That’s what most students do

//myInput.vue <template> <div> <el-input v-model="inputVal" :disabled="disabled"></el-input> </div> </template> <script>  export default { props: { value: { type: String, default: '', }, disabled: { type: Boolean, default: false } }, computed: { inputVal: { get() { return this.value; }, set(val) { this.$emit('input', val); } } } } </script>Copy the code

$attrs = myInput; $attrs = myInput; $attrs = myInput; $attrs = myInput;

$attrs: contains attribute bindings (except class and style) that are not recognized (and retrieved) as prop in the parent scope. When a component does not declare any prop, all parent-scoped bindings (except class and style) are included, and the internal component can be passed in via v-bind=”$attrs”

//myInput.vue
<template>
  <div>
    <el-input v-model="input" v-bind="$attrs"></el-input>
  </div>
</template>
Copy the code

If that’s not enough, we have to set the inheritAttrs option to false. Why? Take a look at the official definition of the inheritAttrs option.

Attribute bindings, which are not recognized as props in the parent scope, are “rolled back” by default and applied to the root element of the child component as normal HTML attributes. This may not always behave as expected when writing a component that wraps one target element or another. These default behaviors are removed by setting inheritAttrs to false. These attributes can be enabled with $attrs and can be explicitly bound to non-root elements by V-bind. Note: This option does not affect the class and style bindings.

In short, set inheritAttrs to false to prevent properties set for the myInput component from being added to the root div of the myInput component.

//myInput.vue
<template>
  <div>
    <el-input v-model="input" v-bind="$attrs"></el-input>
  </div>
</template>
<script>
export default {
  inheritAttrs: false,
  props: {
    value: {
      type: String,
      default: '',
    },
  },
  computed: {
    inputVal: {
      get() {
        return this.value;
      },
      set(val) {
        this.$emit('input', val);
      }
    }
  }
}
</script>
Copy the code

When set this way, properties of the EL-Input component can be used directly on the myInput component, no matter how many additional properties are added to the el-Input component.

Custom events using third-party components

If you use a custom event from the el-Input component on myIpput, your first reaction will probably be this.$emit.

//myInput.vue
<template>
  <div>
    <el-input v-model="input" v-bind="$attrs" @blur="blur"></el-input>
  </div>
</template>
<script>
export default {
  inheritAttrs: false,
  props: {
    value: {
      type: String,
      default: '',
    },
  },
  computed: {
    inputVal: {
      get() {
        return this.value;
      },
      set(val) {
        this.$emit('input', val);
      }
    }
  },
  methods: {
    blur() {
      this.$emit('blur')
    }
  }
}
</script>
Copy the code
<myInput v-model="value" @blur="handleBlur"></myInput>
Copy the code

The $Listeners on the el-Input component have four custom events, but they are not many. How can we add methods one by one to the list of $listeners?

$Listeners: Contains V-on event listeners in the parent scope (without.native modifiers). It can be passed into internal components via V-on =”$Listeners “.

//myInput.vue
<template>
  <div>
    <el-input v-model="input" v-bind="$attrs" v-on="$listeners"></el-input>
  </div>
</template>
Copy the code

Note that you can use custom events on myInput by adding V-on =”$Listeners “to the el-Input component.

Use slots for third-party components

What if the slots defined on the el-Input component were used on the myIpput component? There is not much trickery in this, as the third party component defines as many slots that are exposed with slot tags during encapsulation. For example, to expose the prefix slot in the EL-Input component, the code would look like this:

//myInput.vue
<template>
  <div>
    <el-input v-model="input" v-bind="$attrs" @blur="blur">
      <template #prepend>
        <slot name="prepend"></slot>
      </template>
    </el-input>
  </div>
</template>
Copy the code

Methods of using third-party components

To do this, first add a ref=”elInput” attribute to the el-Input component of the myInput component,

//myInput.vue
<template>
  <div>
    <el-input ref="elInput></el-input>
  </div>
</template>
<script>
export default {
  mounted(){
     this.elInput = this.$refs.elInput;
  }
}
</script>
Copy the code

Here to pay attention to the execution time of father and son component mounted, because generally el – input component is introduced in the global, a synchronization is introduced into components, el – input component mounted at this time than myInput components mounted to perform first, MyInput mounted assigns this.$refs.elInput to a property of myInput’s this.

There are two ways in which the el-Input component can be used by the myInput component, related to the introduction of the myInput component.

Suppose the myInput component is imported synchronously

<template> <div> <myInput ref="myInput"></myInput> </div> </template> <script> import myInput from './myInput.vue'; export default { data() { return { } }, components: {myInput,}, mounted () {/ / call el - input component focus method enclosing $refs. MyInput. ElInput. The focus (); } } </script>Copy the code

Suppose the myInput component is introduced asynchronously

<template> <div> <myInput ref="myInput"></myInput> </div> </template> <script> export default { data() { return { } }, components: { myInput: () => import('./myInput.vue') }, Mounted () {/ / call el - input component focus method setTimeout (() = > {this. $refs. MyInput. ElInput. Focus (); }) } } </script>Copy the code