写文章不容易,点个赞呗兄弟 专一 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工做原理,源码版助于了解内部详情,让咱们一块儿学习吧 研究基于 Vue版本 【2.5.17】node
若是你以为排版难看,请点击 下面连接 或者 拉到 下面关注公众号也能够吧数组
【Vue原理】VModel - 源码版 之 select 详解 dom
今天咱们来看看 v-model 处理 select 有什么特殊的地方函数
前面已经有三篇说明VModel了学习
【Vue原理】VModel - 源码版 之 表单元素绑定流程 code
【Vue原理】VModel - 源码版之input详解 component
经过第一篇源码分享,咱们就知道 Vue是经过 设置 select 的 selectedIndex 来控制选项的,blog
哈哈,如今咱们就是来分析究竟是怎么设置 selectedIndex 的token
好的,咱们必定要带着问题进行学习,这样学完才有用
一、Vue 如何设置 selectedIndex 二、Vue 在哪里设置 selectedIndex
Vue 是经过 一个 setSelected 的方法专门来设置 selectedIndex 的,咱们来看下源码
function setSelected(el, binding, vm) { var selected, option; for (var i = 0, l = el.options.length; i < l; i++) { option = el.options[i]; if (isMultiple) { selected = value.indexOf(option) > -1; if (option.selected !== selected) { option.selected = selected; } } else { if (option.value == value) { if (el.selectedIndex !== i) { el.selectedIndex = i; } return } } } if (!isMultiple) { el.selectedIndex = -1; } }
两处会修改 selectedIndex 的地方我已经加红加粗
简单解释下 setSelected 的做用
一、绑定值没法匹配任何option 时,设置 selectedIndex =-1,而后select 就会显示空
举栗子
select 的 selectedIndex 是-1,而后选择框内显示空
二、选择时,若是多个options 值相等时,只取第一个相等项
举栗子 三个选项的 value 都同样
哈哈,我明明选了3,可是 显示1,这就是 Vue 作的处理,多个相同值的选择,只去第一个
可是这个也是有条件的,必须在 value 变化的时候,才会进行更新,因而才会有 判断操做
好比如今select 的value是 1,你再选择一个 也是 1 的其余选项,Vue 就不会更新,也就不会判断,你选了就选了,无论你了
看图,初始化 select value 为空,而后选择 value 是1 的 第三个选项,
哦豁,忽然变成第一个选项了
而我再选择 3 和 2 的时候,却不会变成 第一个选项,所以 3 和2 的 value 都是 1,value 没有变化,select 不会更新
三、选择后,options 变化,会根据以前的选择,更新它在options的位置
options 改变了,而后把 1 的位置变成最后一个,而后 Vue 就会相应地把 selectedIndex 的位置更新为 新options 中对应的位置
Vue 会在 v-model 的两个钩子函数中更新 select 的 selectedIndex
inserted
当dom被插入到页面中后,会触发这个钩子函数
上一篇详解input咱们已经能知道,inserted 会处理select
看下 inserted 源码(只有select 处理部分)
function inserted(el, binding, vnode, oldVnode) { if (vnode.tag === 'select') { // 设置 select 的selectedIndex 初始值 setSelected(el, binding, vnode.context); // 把options 的value,全都保存到一个数组 el._vOptions = [].map .call(el.options, function(o){ return o.value }); } }
componentUpdated
当组件更新完毕以后,触发 这个钩子
这个钩子函数只针对 select 处理
上 componentUpdated 钩子函数源码
function componentUpdated(el, binding, vnode) { if (vnode.tag === 'select') { setSelected(el, binding, vnode.context); // 这是以前保存的 旧 的 options 的 全部 value 的数组 好比[ 1,2,3] var prevOptions = el._vOptions; // 拿到 如今全部 option 的value 存到数组 var curOptions = el._vOptions = [].map.call(el.options, getValue); // 当 options 变化,并且跟旧option 每一个都不同 if (curOptions.some(function(o, i) { return ! (o==prevOptions[i]) })) { var needReset = el.multiple ? binding.value.some(function(v) { return hasNoMatchingOption(v, curOptions); }) : // 绑定值变化了,并且绑定值 匹配不到 options // hasNoMatchingOption 是匹配 某个值是否在数组中 binding.value !== binding.oldValue && hasNoMatchingOption(binding.value, curOptions); if (needReset) { trigger(el, 'change'); } } }
看源码,这个钩子大概作了两件事
一、当即更新 selectedIndex
为何要当即更新,怕 options 改变了,而 select.selectedIndex 没有变,致使对应上了 新options 的 index 项,上错花轿嫁对郎
[ 1,2,3 ] 选择了第3项, 而后 index=2,值是3
而后 options 数据改变了,变成了 [7,8,9],而 index 仍是2,而显示值 变成了 9
很明显这不符合逻辑啊,必须每次组件更新都要更新selectedIndex
二、更新绑定值
上面 componentUpdated 能够看到会手动触发 change 回调
触发的条件是
一、options 改变,并且跟旧options每一个都不同
二、绑定值也改变
三、新绑定值没法在 新options 中匹配对应值
我也不懂为何要调用一次 select 的 change 回调
要不咱们 一块儿来查一下这个起因吧
首先,change 回调,做用是更新绑定值,难道就是为了更新?
咱们写个例子看一下
两秒以后,会把 绑定值 和 options 同时改变,并且name并不存在arr 中
既然 一开始认为做用更新 绑定值,那咱们看下绑定值更新成了什么鬼
变成了 undefined
的确是更新了绑定值哦,但是为何要更新绑定值为 undefined 呢?想不通.....
上面是从内部去修改 绑定值的,咱们从外部修改看一下,把内部修改的语句注释掉
发现 外部修改绑定值,再改变 options ,外部修改的绑定值是不会随着options变化而更新的哦
话说其实这里我没太想通,也不知道本身想得对不对,感受这里能够讨论一下
根据上面的现象,我说出个人想法
我以为尤大的想法是,从用户角度出发
若是用户没有选择任何option
可是 options 和 绑定值 同时改变,并且绑定值还不匹配options
这个绑定值改变有个毛用啊???
做为表单数据,你本身内部修改绑定值还不匹配任何option
这样,用户根本不知道你修改,他压根没选择,而提交的时候,提交却有数据,这是干毛?
举栗子
选择最爱的水果,options = [ 西瓜,香蕉,番茄 ]
你修改为了 options = [ 橙子,苹果,雪梨 ],还把用户的选择值改为 锤子
用户还不知道你修改了,我喜欢个锤子喜欢
没道理啊是否是,因此最好是 重置为 undefined
也恰好符合 源码的语义 needReset
额,我是这么想的,也不知道对不对,勿喷我,不过我以为个人想法颇有道理啊
若是用户已经选择option
就算options 改变了,那本质上也是没有错的,由于是用户本身选择,就算不匹配新options,因此就不必重置了