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

44次阅读

共计 3476 个字符,预计需要花费 9 分钟才能阅读完成。

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

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

【Vue 原理】VModel – 源码版 之 select 详解

今天我们来看看 v-model 处理 select 有什么特殊的地方

前面已经有三篇说明 VModel 了

【Vue 原理】VModel – 白话版

【Vue 原理】VModel – 源码版 之 表单元素绑定流程

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

通过第一篇源码分享,我们就知道 Vue 是通过 设置 select 的 selectedIndex 来控制选项的,

哈哈,现在我们就是来分析到底是怎么设置 selectedIndex 的

好的,我们一定要带着问题进行学习,这样学完才有用

1、Vue 如何设置 selectedIndex

2、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 的作用

1、绑定值无法匹配任何 option 时,设置 selectedIndex =-1,然后 select 就会显示空

举栗子

select 的 selectedIndex 是 -1,然后选择框内显示空

2、选择时,如果多个 options 值相等时,只取第一个相等项

举栗子
三个选项的 value 都一样

哈哈,我明明选了 3,但是 显示 1,这就是 Vue 做的处理,多个相同值的选择,只去第一个

但是这个也是有条件的,必须在 value 变化的时候,才会进行更新,于是才会有 判断操作

比如现在 select 的 value 是 1,你再选择一个 也是 1 的其他选项,Vue 就不会更新,也就不会判断,你选了就选了,不管你了

看图,初始化 select value 为空,然后选择 value 是 1 的 第三个选项,

哦豁,突然变成第一个选项了

而我再选择 3 和 2 的时候,却不会变成 第一个选项,因此 3 和 2 的 value 都是 1,value 没有变化,select 不会更新

3、选择后,options 变化,会根据之前的选择,更新它在 options 的位置

options 改变了,然后把 1 的位置变成最后一个,然后 Vue 就会相应地把 selectedIndex 的位置更新为 新 options 中对应的位置


在哪里设置 selectedIndex

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');
            }
        }
    }

看源码,这个钩子大概做了两件事

1、立即更新 selectedIndex

为什么要立即更新,怕 options 改变了,而 select.selectedIndex 没有变,导致对应上了 新 options 的 index 项,上错花轿嫁对郎

[1,2,3] 选择了第 3 项,然后 index=2,值是 3

然后 options 数据改变了,变成了 [7,8,9],而 index 还是 2,而显示值 变成了 9

很明显这不符合逻辑啊,必须每次组件更新都要更新 selectedIndex

2、更新绑定值

上面 componentUpdated 可以看到会手动触发 change 回调

触发的条件是

1、options 改变,而且跟旧 options 每个都不一样

2、绑定值也改变

3、新绑定值无法在 新 options 中匹配对应值

我也不懂为什么要调用一次 select 的 change 回调

要不我们 一起来查一下这个原由吧

首先,change 回调,作用是更新绑定值,难道就是为了更新?

我们写个例子看一下

两秒之后,会把 绑定值 和 options 同时改变,而且 name 并不存在 arr 中

既然 一开始认为作用更新 绑定值,那我们看下绑定值更新成了什么鬼

变成了 undefined

的确是更新了绑定值哦,可是为什么要更新绑定值为 undefined 呢?想不通 …..

上面是从内部去修改 绑定值的,我们从外部修改看一下,把内部修改的语句注释掉

发现 外部修改绑定值,再改变 options,外部修改的绑定值是不会随着 options 变化而更新的哦

话说其实这里我没太想通,也不知道自己想得对不对,感觉这里可以讨论一下

根据上面的现象,我说出我的想法

我觉得尤大的想法是,从用户角度出发

如果用户没有选择任何 option

但是 options 和 绑定值 同时改变,而且绑定值还不匹配 options

这个绑定值改变有个毛用啊???

作为表单数据,你自己内部修改绑定值还不匹配任何 option

这样,用户根本不知道你修改,他压根没选择,而提交的时候,提交却有数据,这是干毛?

举栗子

选择最爱的水果,options = [西瓜,香蕉,番茄]

你修改成了 options = [橙子,苹果,雪梨],还把用户的选择值改成 锤子

用户还不知道你修改了,我喜欢个锤子喜欢

没道理啊是不是,所以最好是 重置为 undefined

也刚好符合 源码的语义 needReset

额,我是这么想的,也不知道对不对,勿喷我,不过我觉得我的想法很有道理啊

如果用户已经选择 option

就算 options 改变了,那本质上也是没有错的,因为是用户自己选择,就算不匹配新 options,所以就没必要重置了

正文完
 0