资讯专栏INFORMATION COLUMN

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

leanxi / 2869人阅读

摘要:因为失去焦点之后被强制更新了一波嗯,这就是的作用,把页面上的显示值也过滤一遍

写文章不容易,点个赞呗兄弟
专注 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

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

输入普通字符

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

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

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

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

如果不做!在输入拼音的时候,每打一个拼音字母都会触发 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

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

看下最终的回调

function($event){

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

}

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

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

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

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

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

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

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/105202.html

相关文章

  • Vue原理VModel - 源码版 之 select 详解

    摘要:写文章不容易,点个赞呗兄弟专注源码分享,文章分为白话版和源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧研究基于版本如果你觉得排版难看,请点击下面链接或者拉到下面关注公众号也可以吧原理源码版之详解今天我们来看看处 写文章不容易,点个赞呗兄弟专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧研究基于 ...

    lsxiao 评论0 收藏0
  • Vue原理VModel - 源码版 之 表单元素绑定流程

    摘要:首先,兄弟,容我先说几句涉及源码很多,篇幅很长,我都已经分了上下三篇了,依然这么长,但是其实内容都差不多一样,但是我还是毫无保留地给你了。 写文章不容易,点个赞呗兄弟专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧研究基于 Vue版本 【2.5.17】 如果你觉得排版难看,请点击 下面链接 或者 拉到 下面关注公众号也...

    sarva 评论0 收藏0
  • Vue原理VModel - 白话版

    摘要:执行的时候,会绑定上下文对象为组件实例于是中的就能取到组件实例本身,的代码块顶层作用域就绑定为了组件实例于是内部变量的访问,就会首先访问到组件实例上。其中的获取,就会先从组件实例上获取,相当于。 写文章不容易,点个赞呗兄弟专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧研究基于 Vue版本 【2.5.17】 如果你觉得...

    keke 评论0 收藏0
  • Vue原理】依赖收集 - 源码版之基本数据类型

    摘要:当东西发售时,就会打你的电话通知你,让你来领取完成更新。其中涉及的几个步骤,按上面的例子来转化一下你买东西,就是你要使用数据你把电话给老板,电话就是你的,用于通知老板记下电话在电话本,就是把保存在中。剩下的步骤属于依赖更新 写文章不容易,点个赞呗兄弟专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧研究基于 Vue版本 【...

    VincentFF 评论0 收藏0
  • Vue原理】Slot - 源码版之普通插槽

    摘要:我们都知道分为普通和作用域,两个内容都很多,所以分两部分进行讲述。今天讲的是普通其实普通,表示默认和具名,只是他们的处理方式都差不多,就只是是否有自定义名字而已,所以,表示一种类型。 写文章不容易,点个赞呗兄弟专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧研究基于 Vue版本 【2.5.17】 如果你觉得排版难看,请...

    lansheng228 评论0 收藏0

发表评论

0条评论

leanxi

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<