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

栏目: 编程语言 · 发布时间: 5年前

内容简介:写文章不容易,点个赞呗兄弟专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧研究基于 Vue版本

写文章不容易,点个赞呗兄弟

专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧

研究基于 Vue版本 【2.5.17】

如果你觉得排版难看,请点击 下面链接 或者 拉到 下面 关注公众号 也可以吧

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

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

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

1、预输入延迟更新

2、range 类型的 input

3、v-model.lazy

4、v-model.trim、v-model-number

预输入延迟更新

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

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

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

当composing=true时,事件回调不会走到下面的更新操作,而 Vue 正式通过这个标志位,判断现在是否是预输入而确定是否需要实时更新

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

compositionstart

compositionend

change

开始讲解这三个事件了

1、compositionstart

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

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

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

输入普通字符

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

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

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

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

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

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

为什么要做预输入延迟更新?

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

而此时触发 input 事件没有任何意义,因为还不是我们要输入的值,这是一个浪费的操作

刚好,compositionstart 在 input 之前触发,而且只会预输入才触发

所以!就可以通过一个标志位来控制 input 回调导致的更新就再好不过了!

怎么做呢?看 compositionstart 回调源码

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

2、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);
}

3、change

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

change 的回调 和 compositionend 的回调是一样的,因为只是一个备胎功能

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

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

Vue 官方文档说明 inserted

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

看下 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 转换成数字方法

看下最终的回调

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

function($event){

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

}

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

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

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

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

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

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

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

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

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

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


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

用户体验面面观

用户体验面面观

[美] 库涅夫斯基(Mike Kuniavsky) / 汤海 / 清华大学出版社 / 2010-5 / 69.00

这是一本专注于用户研究和用户体验的经典读物,同时也是一本容易上手的实战手册,从实践者的角度,着重讨论和阐述了用户研究的重要性、主要的用户研究方法和工具,同时借助于实例介绍了相关的应用。全书共3部分18章,深度剖析了何为优秀的用户设计,用户体验包括哪些研究方法和工具,如何 得出和分析用户体验调查结果等。一起来看看 《用户体验面面观》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具