Antecedents feed

Vue + element-UI, an input box to enter the order number, implementing the back end to request update data when pressing enter or the input box loses focus.

Implementation scheme

Define a method searchByOrderId and bind two events to the input field: keyUp and blur, both of which trigger the searchByOrderId

<template>
    <div>
        <el-input
            v-model="orderId"
            placeholder="Please enter the order number for enquiry"
            @keyup.enter.native="searchByOrderId"
            @blur="searchByOrderId"
        />
    </div>
</template>
<script>
export default {
    data () {
        return {
            orderId: ' '
        }
    },
    methods: {
        searchByOrderId() {// use orderId to send requestsif (this.orderId) {
                axios.get(this.orderId)
            }
        }
    }
}
</script>
Copy the code

Existing problems

  1. After entering 1, press Enter continuously, the request will be sent once by pressing Enter.
  2. After entering 1, press Enter to send the request once, and then send it again after losing focus.

In general, if the orderId does not change, the request should be sent once, but it is sent multiple times.

The solution

Define one more data: oldOrderId, which holds the previous orderId. The request will be resend only if the values are different twice.

data () {
    return {
        orderId: ' ',
        oldOrderId: ' '
    }
},
methods: {
    searchByOrderId () {
        if(this.orderId && this.oldOrderId ! == this.orderId) {this.oldorderId = this.orderId // Update oldOrderId axios.get(this.orderId)}}Copy the code

The new problem

If a user copies a segment of orderId to query, but the interface error (dialog) is displayed, he closes the dialog error message, and tries again, but does not send the request. (Because orderId has not changed)

The actual code will be restored to the beginning (or you can make a similar throttling), but some colleagues said it was excessive optimization and unnecessary (ordinary users will not always enter, malicious personnel will not manually burst the interface).

That’s the end of the example. Let’s take a closer look at the code

Let’s take a look at the el-input structure in the elder-UI source code.

<template>
    <div class="el-input">
        <input
            type="text"
            v-bind="$attrs"
            @input="handleInput"
            @focus="handleFocus"
            @blur="handleBlur"
            @change="handleChange"
        />
    </div>
</template>
<script>
export default {
    inheritAttrs: false// The function does$emit{handleInput handleFocus handleBlur handleChange}} </script>Copy the code

We’ll see that it’s actually a DIV wrapped around an input. In our project, we bidirectionally bind the orderId to the EL-Input component, passing a placeholder property. None of the el-Input components are processed, but it works.

$attrs

You can see that there is a V-bind =”$attrs” on the el-input in the element-UI and an inheritAttrs: false in the script

Vue documentation for $attrs cn.vuejs.org/v2/guide/co…

Vue has both prop and non-prop features.

Typically we write prop features: the parent component passes props to the child component, and the child component uses props to receive and define their types and default values, and so on.

A non-prop feature is one that is passed to a component that does not have the corresponding prop defined feature. (Generally speaking, features passed by a parent component to a child component that are not defined in the props of the child component)

Normally all non-prop features of the parent component are bound directly to the root element of the child component. (The prop feature is bound to any element because it is received in the props subcomponent)

$attrs is all non-prop features (except class and style). (Is an object)

// Parent component <child class="cls"
    style="color: red"
    placeholder="Please enter the order number for enquiry"
    val="111"
    a="222"/> // Subcomponent <div class="el-input">
    <input
      type="text"
    />
</div>
<script>
    export default {
        props: ['a']
    }
</script>
Copy the code

The final child component is rendered as

<div 
    class="el-input cls"
    style="color: red;"
    placeholder="Please enter the order number for enquiry"
    val="111"
"> text" />
</div>
Copy the code

The child component declares porps: [‘a’] so there is no A

So porP features are: A, non-prop features are: class, style, placeholder, val.

$attrs = {placeholder: ‘please input the order number ‘, value: ‘111’}

You can see that all non-prop features are tied directly to the root element of the child component.

The above is the default

But if you don’t want the component’s root element to inherit non-prop supported features, you can set inheritAttrs: False in the component’s options. Note that the inheritAttrs: false option does not affect the style and class bindings.

With inheritAttrs: false and $attrs, you can manually determine which element these attributes will be assigned to. This is often used when writing the underlying components. (See El-Input in Elder-UI.)

// Parent component <child class="cls"
    style="color: red"
    placeholder="Please enter the order number for enquiry"
    val="111"
    a="222"/> // Subcomponent <div class="el-input">
    <input
      type="text"
      v-bind="$attrs"
    >
</div>
<script>
    export default {
        inheritAttrs: false,
        props: ['a']
    }
</script>
Copy the code

The child component is rendered as

<div 
    class="el-input cls" 
    style="color: red;
">
    <input 
        type="text"
        placeholder="Please enter the order number for enquiry"
        val="111"
    />
</div>
Copy the code

tips:

obj={a: 'aa', b: 'bb', c: 'cc'}

<div v-bind="obj"></div> // is equivalent to <div a="aa"
    b="bb"
    c="cc"
></div>

Copy the code

Now we know why the placeholder in business code works. (This is a non-prop feature and is manually bound to the input of el-Input, not the root div.)

Then examine why events bound to child components can be executed.

<template>
  <div>
    <el-input
      v-model="orderId"
      placeholder="Please enter the order number for enquiry"
      @keyup.enter.native="searchByOrderId"
      @blur="searchByOrderId"
    />
  </div>
</template>
Copy the code

The el-Input component binds the input focus blur change event to the input component and emits to the parent component, so blur can be bound normally in our example. But no keyup event is emitted. In this example, the keyUP event is bound with Native.

.native

Listen for a native event directly on the root element of a component

  1. Don’t have tonativeListen for child component click events
// Parent component < child-@myclick ="handleClick"
/>
handleClick () {
    console.log('father handleClick'} // Subcomponent <div @click="handleClick">
    child child child child
</div>
handleClick () {
    this.$emit('myClick')}Copy the code
  1. usenativeListen for child component click events (same effect as above)
// parent < child-@click. native="handleClick"
/>
handleClick () {
    console.log('father handleClick'<div> child child child </div>Copy the code

You can see that using Native saves the child component from binding the click event itself and emits to the parent component $.

The usage scenario is similar to the example above. If the child click does nothing extra, it simply emits the corresponding event to the parent. At this point, you can use Native to simplify listening for native events.

In the above element-UI example, we listen for the native KeyUp event directly on the EL-Input component: < el-Input.keyup.enter. native=”searchByOrderId” />. The el-Input component is a div wrapped around an input. We should listen for the keyUp event on the input, but since the keyup event bubbles over the div, there is no extra action.

However, some events must be listened on to the input manually. $Listeners are shining on stage.

$listeners

To solve this, native can only listen for a native event directly on the root element. Vue provides $Listeners. The $Listeners are also an object that contains event listeners (without the.native modifier) that operate on the component. You can manually determine which element of the child component can trigger the event listener of the parent component. Cn.vuejs.org/v2/guide/co…

$Listeners also enable intergenerational component communication.

// Parent < child1@mytest1 ="handleMyTest1"
    @myTest2="handleMyTest2"Child1 <div> <div>child1 child1</div> <child2 V-on ="$listeners"/> </div> // Child component 1 child2. This component is omitted$emit('myTest1') $emit('myTest2')
<div>
    <div>child2 child2</div>
    <div @click="handleClick">
        child222 child222
    </div>
</div>
<script>
    export default {
        methods: {
            handleClick () {
                this.$emit('myTest1')
                this.$emit('myTest2')
            }
        }
    }
</script>
Copy the code

$Listeners can save layer after layer of $listeners from emit certain events.

Now that two layers are nested, it’s much easier to have multiple layers and pass multiple events. Of course, it’s best to use EventBus and Vuex for special situations.