【Vue原理】VModel - 源码版之input详解

写文章不容易,点个赞呗兄弟
专一 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工做原理,源码版助于了解内部详情,让咱们一块儿学习吧
研究基于 Vue版本 【2.5.17】

若是你以为排版难看,请点击 下面连接 或者 拉到 下面关注公众号也能够吧vue

【Vue原理】VModel - 源码版之input详解 node

上一篇文章,咱们大概讲了全部表单元素的双绑原理,可是仍然有两个特殊的表单元素,是要多更多处理的,也不可能放在一篇文章说完,今天,咱们说的是 input 的特殊处理的地方浏览器

而 input 有什么特殊处理的地方呢?函数

一、预输入延迟更新学习

二、range 类型的 inputurl

三、v-model.lazyspa

四、v-model.trim、v-model-numbercode


预输入延迟更新

先来看看Vue 给 input 正常绑定的回调token

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

看到我标红的地方,这句话就是完成预输入延迟更新的重点事件

当composing=true时,事件回调不会走到下面的更新操做,而 Vue 正式经过这个标志位,判断如今是不是预输入而肯定是否须要实时更新

首先,Vue 会为 input 或者 textarea 绑定如下事件

compositionstart
compositionend
change
开始讲解这三个事件了

一、compositionstart

首先,compositionstart 会在 input 事件触发以前 触发

but!你打一些直接输入的字符,是不会触发 compositionstart 的,只会触发 input

只有打预输入的字符才会触发,好比 输入拼音,不行看图

输入普通字符

image

预输入延迟更新下,输入拼音

image

取消预输入延迟更新,输入拼音

image

看完上面的动图,预输入延迟更新什么用,估计你内心也有点逼数了吧?

为何要作预输入延迟更新?

若是不作!在输入拼音的时候,每打一个拼音字母都会触发 input 事件,可是咱们根本还没往表单中写入咱们预想中的东西

而此时触发 input 事件没有任何意义,由于还不是咱们要输入的值,这是一个浪费的操做

恰好,compositionstart 在 input 以前触发,并且只会预输入才触发

因此!就能够经过一个标志位来控制 input 回调致使的更新就再好不过了!

怎么作呢?看 compositionstart 回调源码

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

二、compositionend

在打完预输入的字符以后,会触发

在预输入延迟更新中起什么做用呢?

预输入结束,确定是设置 composing 为 false了,看源码

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

还会 手动触发 input 事件去 执行更新操做哦

trigger 是什么?也是很简单的,值得收藏的源码,不解释了

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

三、change

为了兼容Safari<10.2 等那些 不会触发 compositionend 的浏览器(Vue本身注释说的,我没有测过),因而监听 change事件,来代替 compositionend 的功能

change 的回调 和 compositionend 的回调是同样的,由于只是一个备胎功能

四、 他们在哪里开始绑定这些事件呢?

你应该必须知道,指令都是有生命钩子函数的,而这几个事件正是在 inserted 钩子中进行绑定的

Vue 官方文档说明 inserted

image

看下 inserted 钩子函数

function inserted(el, binding, vnode, oldVnode) {   

    // isTextInputType判断 input 是否是 text,number,password,search,email,tel,url其中一个
    if (vnode.tag === 'textarea' || isTextInputType(el.type)) {
        el._vModifiers = binding.modifiers;        
        // 若是设置 v-model.lazy,那么不处理 预输入的问题
        if (!binding.modifiers.lazy) {
            el.addEventListener('compositionstart', onCompositionStart);
            el.addEventListener('compositionend', onCompositionEnd);
            el.addEventListener('change', onCompositionEnd);
        }
    }
}

TIP

inserted 钩子中,还处理了 select ,可是这里是input的版块,因此去掉了,放在下篇文章讲


Range 类型 Input

为了兼容 IE,因此在解析的时候,先保存的是 __r 事件,后面开始绑定的时候,判断浏览器而决定使用什么事件

function genDefaultModel(
    el, value,  modifiers

){    
    var type = el.attrsMap.type;    
    var ref = modifiers || {};    
    var lazy = ref.lazy;   

    // 这里省略了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);
}

看到我加蓝加粗的地方,就是为 range 特别处理的地方,绑定 __r 事件

判断浏览器,转换事件的函数updateDOMListeners源码

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 in on) {
        vnode.elm.addEventListener(name, on[name]);
    }
}

v-model.lazy

当你的 v-model 设置了 lazy 的时候,会绑定 change 而不是 input,延时更新的意思

function genDefaultModel(
    el, value, modifiers

){    

    var ref = modifiers || {};    
    var lazy = ref.lazy;    

    // 省略了 range 类型的判断
    var event = lazy ? 'change' :'input';
    addHandler(el, event, code, null, true);
}

咱们都知道,为了输入实时响应,vue 默认为 input 等输入类型的 表单 绑定 input 事件,让 input

若是你设置延迟更新,就是至关于你改变了内容,而后失去焦点才触发


v-model.trim、v-model.number

若是你给 v-model 设置了值过滤,像 trim 去掉首尾空格,number 把值变成数字

function genDefaultModel(
   el, value, modifiers
){    

    var ref = modifiers || {}; 
    var number = ref.number;    
    var trim = ref.trim;  


    // 去首尾空格
    if (trim) {
        valueExpression = "$event.target.value.trim()";
    }    

    // 转成数字
    if (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()');
    }   

}

对于 trim 和 number,Vue 会对表单值作处理,你能够看到源码中

trim:值会调用 trim 方法

number:会调用 _n 转换成数字方法

看下最终的回调

image

function($event){

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

}

image

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

这两个鬼东西,还会额外绑定一个事件 blur

看下回调 $forceUpdate,这个函数做用是强制更新页面

为何要更新页面?给个动图看好吧

image

image

我设置了 trim,而后输入的时候,故意多加几个空格,而后失去焦点(触发设置的 blur),再点发现空格不见了。由于失去焦点以后被强制更新了一波

嗯,这就是 $forceUpdate 的做用,把页面上的显示值也过滤一遍

公众号

相关文章
相关标签/搜索