Writing an article is not easy, click a like brother focus on Vue source code sharing, the article is divided into vernacular version and source version, vernacular version to help understand the working principle, source version to help understand internal details, let us study together based on Vue version [2.5.17]

If you think the layout is ugly, please click the link below or pull to the following public account can also be

Vue principle VModel – source version of the input details

In the last article, we covered the principle of double binding for all form elements, but there are still two special form elements that require more processing and cannot be put into a single article. Today, we’ll focus on special processing for input

What’s special about input?

1. Preinput delay update

2. Input of range type

3, the v – model. Lazy

4, V-model. Trim, V-model number


Preinput delay update

Let’s take a look at Vue’s callback to input properly bound

input: function($event) {     
    if ($event.target.composing) return;
    name = $event.target.value
}
Copy the code

See where I’ve marked red, this is the key to completing the preinput deferred update

In composing=true, event callbacks do not enter into the following update operations, but Vue formally uses this flag bit to determine whether real-time updates are required to determine whether an event is currently pre-entered

First, Vue binds the following events to the input or textarea

Compositionstart compositionEnd change starts explaining these three events

1, compositionstart

First, compositionStart fires before the input event fires

But!!! If you type something directly, it doesn’t trigger compositionStart, it just triggers input

Only hit the pre-input character will trigger, such as input pinyin, not look at the picture

Enter ordinary characters

Preinput delay updates under input pinyin

Cancel preinput delay update, input pinyin

After seeing the above GIF, what is the use of preinput delay update?

Why do I need to do preinput delayed update?

If not done! When typing in pinyin, the input event is triggered for each pinyin letter, but we haven’t written anything into the form at all

It doesn’t make sense to trigger the input event because it’s not the value we’re entering yet, which is a wasteful operation

As it happens, compositionStart fires before input, and only for pretyping

So! It would be nice to have a flag bit to control the update caused by the input callback!

So how do we do that? Look at the compositionStart callback source code

function onCompositionStart(e) {
    e.target.composing = true;
}
Copy the code

2, compositionend

Fires after typing a pretyped character

What does it do in preinput deferred updates?

Composing is complete. I must have configured my composing to false

function onCompositionEnd(e,eventname) {    
    if(! e.target.composing) {return }
    e.target.composing = false;
    trigger(e.target, 'input');
}
Copy the code

The input event is manually triggered to perform the update

What is trigger? It is also very simple, worth collecting the source code, do not explain

function trigger(el, type) {    
    var e = document.createEvent('HTMLEvents');
    e.initEvent(type.true.true);
    el.dispatchEvent(e);
}
Copy the code

3, change

In order to be compatible with Safari<10.2 and other browsers that don’t trigger CompositionEnd (Vue notes it, but I didn’t test it), listen for the change event instead of compositionEnd

The change callback is the same as the compositionEnd callback because it is a back-up feature

4. Where do they start binding these events?

You should know that directives have live hook functions, and these events are bound in the INSERTED hook

Vue Official document description inserted

Look at the next inserted hook function

functionInserted (el, binding, vnode, oldVnode) {/ / isTextInputType figure out whether the input text, number, password, search, email, tel, url is one of themif (vnode.tag === 'textarea'|| isTextInputType(el.type)) { el._vModifiers = binding.modifiers; // If v-model.lazy is set, the pre-input problem is not handledif(! binding.modifiers.lazy) { el.addEventListener('compositionstart', onCompositionStart);
            el.addEventListener('compositionend', onCompositionEnd);
            el.addEventListener('change', onCompositionEnd); }}}Copy the code

TIP

Inserted Hook, select is also handled, but this is the input section, so get rid of it and leave it to the next article


The Input Range type

In order to be compatible with IE, so when parsing, first save __r events, and then start binding, decide which events to use depending on the browser

function genDefaultModel(
    el, value,  modifiers

){    
    var type= el.attrsMap.type; var ref = modifiers || {}; var lazy = ref.lazy; Var event =type= = ='range' ? "__r" :'input';

    code = "if($event.target.composing)return;" 
            + value+= ""$event.target.value";
    addProp(el, 'value', ("(" + value + ")"));
    addHandler(el, event, code, null, true);
}

Copy the code

See where I’ve added blue and bold, which is specifically handled for range, binding __r events

Function updateDOMListeners to determine the browser and convert events

function updateDOMListeners(oldVnode, vnode) {    

    var on = vnode.data.on    
    if (isDef(on["__r"])) {        
        var event = isIE ? 'change': 'input';
        on[event] = [].concat(on["__r"], on[event] || []);        
        delete on["__r"];
    }    
    for (name inon) { vnode.elm.addEventListener(name, on[name]); }}Copy the code

v-model.lazy

When your V-model is set to lazy, it will bind change instead of input

functiongenDefaultModel( el, value, modifiers ){ var ref = modifiers || {}; var lazy = ref.lazy; Var event = lazy?'change' :'input';
    addHandler(el, event, code, null, true);
}
Copy the code

As we all know, in order to enter a real-time response, VUE defaults to forms of input type such as Input, binding input events to make input

If you set delayed updates, you change the content and then lose focus


V – model. Trim, v – model. Number

If you set values to filter the V-model, trim removes the leading and trailing Spaces, and number changes the values to numbers

functiongenDefaultModel( el, value, modifiers ){ var ref = modifiers || {}; var number = ref.number; var trim = ref.trim; // remove the first and last Spacesif (trim) {
        valueExpression = "$event.target.value.trim()"; } // Convert to a numberif (number) {
        valueExpression = "_n(" + valueExpression + ")";
    }
    
    code = "if($event.target.composing)return;" +
            value+"="+valueExpression;
    
    addProp(el, 'value', ("(" + value + ")"));
    addHandler(el, "input", code, null, true); 
    
    if (trim || number) {
        addHandler(el, 'blur'.'$forceUpdate()'); }}Copy the code

For trim and number, Vue handles form values, as you can see in the source code

Trim: The value invokes the trim method

Number: the _n to number method is called

Look at the final callback

function(event.target.composing)return; name=_n($event.target.value); }

function($event) {if($event.target.composing)return;
    name=$event.target.value.trim();
}
Copy the code

These two things will also bind an additional event blur

Look at the $forceUpdate callback, which forces the page to be updated

Why update the page? Let’s get a GIF of it

I set trim and intentionally added a few Spaces when typing, then lost focus (triggering the Settings with blur) and clicked on it to find that the Spaces were missing. Because there was a forced update wave after losing focus

Well, that’s what $forceUpdate does, filtering the values displayed on the page as well