内容简介:写文章不容易,点个赞呗兄弟专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧研究基于 Vue版本
写文章不容易,点个赞呗兄弟
专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧
研究基于 Vue版本 【2.5.17】
如果你觉得排版难看,请点击 下面链接 或者 拉到 下面 关注公众号 也可以吧
上一篇文章,我们大概讲了所有表单元素的双绑原理,但是仍然有两个特殊的表单元素,是要多更多处理的,也不可能放在一篇文章说完,今天,我们说的是 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 的作用,把页面上的显示值也过滤一遍
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- JDK SPI源码详解
- 【zookeeper源码】启动流程详解
- 详解RunLoop之源码分析
- 详解CopyOnWrite容器及其源码
- React Scheduler 源码详解(1)
- React Scheduler 源码详解(2)
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。