_index.js
單選框內部一共有三個組件:el-radio
、el-radio-button
和el-radio-group
,我們將一一進行講解。
import Radio from './src/radio';
import RadioButton from './src/radio-button.vue';
import RadioGroup from './src/radio-group.vue';
export default function(Vue) {
Vue.component(Radio.name, Radio);
Vue.component(RadioButton.name, RadioButton);
Vue.component(RadioGroup.name, RadioGroup);
};
export { Radio, RadioButton, RadioGroup };
radio-group
radio-group
的實現十分簡單,就是一個div.radio-group
包裹著一個slot
。
<template>
<div class="el-radio-group">
<slot></slot>
</div>
</template>
script
包含一系列改變樣式的prop
。
props: {
size: String, // Radio 按鈕組尺寸
fill: { // 按鈕激活時的填充色和邊框色
type: String,
default: '#20a0ff'
},
textColor: { // 按鈕激活時的文本顏色
type: String,
default: '#fff'
}
},
watch
上監聽了value
,它會觸發change
事件并且向父組件傳遞el.form.change
事件。
watch: {
value(value) {
this.$emit('change', value);
this.dispatch('ElFormItem', 'el.form.change', [this.value]);
}
}
其中dispatch
是從emitter
這一mixin
中引入的,我們將在mixin篇
中進行講解,簡單的說,這是用來模擬vue1.0
中向父組件傳播事件的$dispatch
的。
mixins: [Emitter],
radio
radio
組件其實也先相對簡單,它同樣引入了emitter
這一mixin
,我們將按照從外往里的順序進行分析。
label
首先最外面是一個label
標簽作為包裹。
<label class="el-radio">
</label>
input
然后,是作為input
的span
部分,它包含一個用來表示選中效果的span
和一個不可以見的input
用來模擬原生的radio
。
最外層el-radio__input
最外層是span.el-radio__input
,上面有一些動態class
,來調整樣式,我們也將一一進行講解。
<span class="el-radio__input"
:class="{
'is-disabled': disabled,
'is-checked': model === label,
'is-focus': focus
}"
>
</span>
disabled
disabled
是一個簡單的Boolean
型的prop
,會影響是否可以選中。
props:{
disabled: Boolean,
}
label
label
是radio
選中時的value
,也是通過prop
傳遞的。
props: {
label: {},
}
model
model
是一個計算屬性,也不是很復雜,就是用來實現v-model
的。
computed: {
model: {
get() {
// 如果父組件是radio-group,返回父組件的value,否則返回自己的value
return this.isGroup ? this._radioGroup.value : this.value;
},
set(val) {
if (this.isGroup) {
// 如果父組件是 radio-group,派發input事件,讓父組件去 emit input 事件
this.dispatch('ElRadioGroup', 'input', [val]);
} else {
// 否則自己 emit input 事件
this.$emit('input', val);
}
}
}
}
其中isGroup
是另一個計算屬性,用來一直向上尋找,看看有沒有父組件乃至祖先組件是radio-group
。
computed: {
isGroup() {
let parent = this.$parent;
while (parent) {
if (parent.$options.componentName !== 'ElRadioGroup') {
parent = parent.$parent;
} else {
this._radioGroup = parent;
return true;
}
}
return false;
},
}
focus
focus
是一個data
屬性,會在原生的input
的獲得焦點和失去焦點時被改變。
el-radio__inner
el-radio__inner
是用來實現選中效果的,通過css
控制。
<span class="el-radio__inner"></span>
input
最后是一個input
來模擬原生的radio
,上面進行了一些簡單的處理,并綁定了相應的數據。
<input
class="el-radio__original"
:value="label"
type="radio"
v-model="model"
@focus="focus = true"
@blur="focus = false"
:name="name"
:disabled="disabled">
radio-button
radio-button
和radio
基本上一樣。
label
最外面仍然是一個label
。
<label
class="el-radio-button"
:class="[
size ? 'el-radio-button--' + size : '',
{ 'is-active': value === label }
]"
>
</label>
size
size
是一個計算屬性,它直接使用了沿著父組件向上尋找到的radio-group
上設置的size
。
computed: {
size() {
return this._radioGroup.size;
}
}
_radioGroup
也是一個計算屬性,它將一直向上尋找radio-group
:
computed: {
_radioGroup() {
let parent = this.$parent;
while (parent) {
if (parent.$options.componentName !== 'ElRadioGroup') {
parent = parent.$parent;
} else {
return parent;
}
}
return false;
},
}
label
label
是一個prop
,用來表示選中該按鈕時的value
值。
props: {
label: {},
}
value
value
是用來實現v-model
的,來獲取選擇的按鈕,從其代碼可以看出來,radio-button
和radio
不同,它不能脫離radio-group
單獨使用:
computed: {
value: {
get() {
return this._radioGroup.value;
},
set(value) {
this._radioGroup.$emit('input', value);
}
},
}
input
然后是用來模擬原生radio
的input
,上面綁定了相應的數據:
<input
class="el-radio-button__orig-radio"
:value="label"
type="radio"
v-model="value"
:name="name"
:disabled="disabled">
el_radio-button__inner
最后是真正會顯示的按鈕span
即其樣式,它內部的值可以通過匿名的默認slot
或者label
進行設置,前者具有更高的優先級:
<span class="el-radio-button__inner" :style="value === label ? activeStyle : null">
<slot></slot>
<template v-if="!$slots.default">{{label}}</template>
</span>
可以看出上面還綁定了一個動態的style
,它通過判斷value === label
決定當前按鈕是否被選中,然后應用activeStyle
,而這個activeStyle
也是一個計算屬性,簡單的說它會根據父級radio-group
的屬性來設置樣式:
computed: {
activeStyle() {
return {
backgroundColor: this._radioGroup.fill,
borderColor: this._radioGroup.fill,
color: this._radioGroup.textColor
};
},
}