简介
单选框这个组件看似简单,实则知识点众多,较为复杂,如果写一个html的原生单选框,那确实很简单,但是封装一个完整的单选组件就不那么简单了,接下来我们先介绍Vue的单选框的一些原理,然后再分析Element的单选框实现
原生单选 Vs Vue单选
原生单选框很简单,如果我们要实现一个男女性别的单选按钮组,代码只需如下几句
<input type="radio" name="sex" value="male" checked>男</input>
<input type="radio" name="sex" value="female">女</input>
复制代码
上面的男的单选按钮添加了checked
属性,表示被选中,value
属性表示单选按钮的值,可以给每个input添加onchange
和onclick
事件来通过点击获取其值,也可以通过一个按钮点击后遍历所有单选的input按钮,获取checked
属性为true
的那一项,然后再获取其value
注意如何让一组单选互斥,也就是说同一时刻只能有一个单选被选中,name
属性就是这个作用, 通过把一些单选按钮的name
设置为同一个值,就达到了互斥的效果
而Vue的单选框则有所不同,代码如下
v-model
即可达到互斥效果,
v-model
的值是data里面的数据,进行了双向绑定,由此可见并没有通过
name
属性来达到互斥,那么时怎么实现的呢?首先先来了解下v-model的本质,v-model本质上是语法糖
function genRadioModel (
el: ASTElement,
value: string,
modifiers: ?ASTModifiers
) {
const number = modifiers && modifiers.number
let valueBinding = getBindingAttr(el, 'value') || 'null'
valueBinding = number ? `_n(${valueBinding})` : valueBinding
addProp(el, 'checked', `_q(${value},${valueBinding})`)
addHandler(el, 'change', genAssignmentCode(value, valueBinding), null, true)
}
复制代码
上述代码是处理单选框model的代码,genRadioModel
参数中的value
就是input的value的值,而valueBinding
的值就是v-model中的v-bind:value的值
<input type="radio" id="jack" value="Jack" v-model="name">
复制代码
如果示例如上,那么addProp
这个方法就会把checked
属性的值_q('Jack',name)
放入属性列表,这里_q是looseEqual
方法的简写,表示宽松比较(如果是对象,则通过JSON.stringify转成字符串比较,否则直接String()转换比较)2个值是否相同,这样这里的逻辑就明确了,如果单选框的value的值和v-model的值相同,那么就加上一个checked
属性,表示该单选被选中,自然而然其他单选框value的值和v-model的值不同,所以就不是选中状态,没有checked属性,所以达到了互斥效果
源码分析
整个单选组件的源码不算太长,但是里面知识点很多,先上源码,官网代码点此
<template>
<label
class="el-radio"
:class="[
border && radioSize ? 'el-radio--' + radioSize : '',
{ 'is-disabled': isDisabled },
{ 'is-focus': focus },
{ 'is-bordered': border },
{ 'is-checked': model === label }
]"
role="radio"
:aria-checked="model === label"
:aria-disabled="isDisabled"
:tabindex="tabIndex"
@keydown.space.stop.prevent="model = isDisabled ? model : label"
>
<span class="el-radio__input"
:class="{
'is-disabled': isDisabled,
'is-checked': model === label
}"
>
<span class="el-radio__inner"