源碼如下:
<template>
<div
class="el-switch"
:class="{ 'is-disabled': switchDisabled, 'is-checked': checked }"
role="switch"
:aria-checked="checked"
:aria-disabled="switchDisabled"
@click="switchValue"
>
<input
class="el-switch__input"
type="checkbox"
@change="handleChange"
ref="input"
:id="id"
:name="name"
:true-value="activeValue"
:false-value="inactiveValue"
:disabled="switchDisabled"
@keydown.enter="switchValue"
>
<span
:class="['el-switch__label', 'el-switch__label--left', !checked ? 'is-active' : '']"
v-if="inactiveIconClass || inactiveText">
<i :class="[inactiveIconClass]" v-if="inactiveIconClass"></i>
<span v-if="!inactiveIconClass && inactiveText" :aria-hidden="checked">{{ inactiveText }}</span>
</span>
<span class="el-switch__core" ref="core" :style="{ 'width': coreWidth + 'px' }"></span>
<span
:class="['el-switch__label', 'el-switch__label--right', checked ? 'is-active' : '']"
v-if="activeIconClass || activeText">
<i :class="[activeIconClass]" v-if="activeIconClass"></i>
<span v-if="!activeIconClass && activeText" :aria-hidden="!checked">{{ activeText }}</span>
</span>
</div>
</template>
<script>
import Focus from 'element-ui/src/mixins/focus';
import Migrating from 'element-ui/src/mixins/migrating';
export default {
name: 'ElSwitch',
mixins: [Focus('input'), Migrating],
// 注入elForm對象,防止不和el-form使用時對象不存在的問題。
inject: {
elForm: {
default: ''
}
},
props: {
value: {
type: [Boolean, String, Number],
default: false
},
disabled: { //是否禁用
type: Boolean,
default: false
},
width: { //switch 的寬度(像素)
type: Number,
default: 40
},
activeIconClass: { //switch 打開時所顯示圖標的類名,設置此項會忽略 active-text
type: String,
default: ''
},
inactiveIconClass: { //switch 關閉時所顯示圖標的類名,設置此項會忽略 inactive-text
type: String,
default: ''
},
activeText: String, //switch 打開時的文字描述
inactiveText: String, //switch 關閉時的文字描述
activeColor: { //switch 打開時的背景色
type: String,
default: ''
},
inactiveColor: { //switch 關閉時的背景色
type: String,
default: ''
},
activeValue: { //switch 打開時的值
type: [Boolean, String, Number],
default: true
},
inactiveValue: { //switch 關閉時的值
type: [Boolean, String, Number],
default: false
},
name: { //switch 對應的 name 屬性
type: String,
default: ''
},
id: String
},
data() {
return {
coreWidth: this.width
};
},
created() {
if (!~[this.activeValue, this.inactiveValue].indexOf(this.value)) {
this.$emit('input', this.inactiveValue);
}
},
computed: {
//當前的開關組件的狀態
checked() {
//父組件中v-model綁定的值是否等於switch 打開時的值
return this.value === this.activeValue;
},
//當前組件是否被禁用
switchDisabled() {
return this.disabled || (this.elForm || {}).disabled;
}
},
watch: {
checked() {
this.$refs.input.checked = this.checked;
//在用戶設置了active-color和inactive-color時,通過setBackgroundColor設置開關的背景色
if (this.activeColor || this.inactiveColor) {
this.setBackgroundColor();
}
}
},
methods: {
handleChange(event) {
//!this.checked為true,則表示當前是this.value === this.inactiveValue,即為關着的狀態;需要切換為開着的狀態,返回this.activeValue
this.$emit('input', !this.checked ? this.activeValue : this.inactiveValue);
this.$emit('change', !this.checked ? this.activeValue : this.inactiveValue);
this.$nextTick(() => {
//修改value值並不是立即生效,而且為了防止父組件未修改值,這里進行了重復賦值
this.$refs.input.checked = this.checked;
});
},
//在用戶設置了active-color和inactive-color時,點擊切換開關時,根據this.checked的值設置開關的背景顏色
setBackgroundColor() {
//如果 this.checked為true,即當前switch是打開,開關返回打開時設置的背景色
let newColor = this.checked ? this.activeColor : this.inactiveColor;
this.$refs.core.style.borderColor = newColor;
this.$refs.core.style.backgroundColor = newColor;
},
switchValue() {
//在不禁用的狀態下才能點擊
!this.switchDisabled && this.handleChange();
},
getMigratingConfig() {
return {
props: {
'on-color': 'on-color is renamed to active-color.',
'off-color': 'off-color is renamed to inactive-color.',
'on-text': 'on-text is renamed to active-text.',
'off-text': 'off-text is renamed to inactive-text.',
'on-value': 'on-value is renamed to active-value.',
'off-value': 'off-value is renamed to inactive-value.',
'on-icon-class': 'on-icon-class is renamed to active-icon-class.',
'off-icon-class': 'off-icon-class is renamed to inactive-icon-class.'
}
};
}
},
mounted() {
/* istanbul ignore if */
this.coreWidth = this.width || 40;
if (this.activeColor || this.inactiveColor) {
this.setBackgroundColor();
}
this.$refs.input.checked = this.checked;
}
};
</script>
解析:
(1)組件的html結構
<div class="el-switch">
<input class="el-switch__input" type="checkbox">
<!--顯示左邊的標簽-->
<span class="el-switch__label el-switch__label--left">
<i></i>
<span></span>
</span>
<!--中間的開關-->
<span class="el-switch__core"></span>
<!--顯示右邊的標簽-->
<span class="el-switch__label el-switch__label--right">
<i></i>
<span></span>
</span>
</div>
input標簽被隱藏掉了,css部分代碼如下:
.el-switch__input {
position: absolute;
width: 0;
height: 0;
opacity: 0;
margin: 0;
}
如果把上面的樣式代碼注釋掉,如圖所示:
通過 <input type="checkbox">
的checked屬性來控制文字顯示以及開關的狀態切換。最外層包裹的div是為了能夠通過點擊文字也能切換開關狀態。
(2)混入的 mixins: [Focus('input'), Migrating]
主要是migrating.js,該文件主要是用於開發環境下提示一些遷移或者即將修改的屬性和方法的。
示例:我用的element-ui v2.4.9,我按下面這樣寫,off-text屬性在我當前的版本中已經被改為inactive-text
<el-switch v-model="value2" off-text="關着"></el-switch>
當我運行之后在控制台輸出:
所有遷移的屬性在組件的getMigratingConfig()方法中:
getMigratingConfig() {
return {
props: {
'on-color': 'on-color is renamed to active-color.',
'off-color': 'off-color is renamed to inactive-color.',
'on-text': 'on-text is renamed to active-text.',
'off-text': 'off-text is renamed to inactive-text.',
'on-value': 'on-value is renamed to active-value.',
'off-value': 'off-value is renamed to inactive-value.',
'on-icon-class': 'on-icon-class is renamed to active-icon-class.',
'off-icon-class': 'off-icon-class is renamed to inactive-icon-class.'
}
};
}
(3)created方法
created() {
//如果用戶傳入的v-model的值既不是activeValue也不是inactiveValue時,將inactiveValue傳遞出去,開關處於關狀態
if (!~[this.activeValue, this.inactiveValue].indexOf(this.value)) {
this.$emit('input', this.inactiveValue);
}
},
~代表按位非運算符,如果[this.activeValue, this.inactiveValue].indexOf(this.value)為-1,則按位非后變為0。
參考博文:https://juejin.im/post/5b861db0e51d4538aa1b5630
http://www.zhuyuntao.cn/2018/10/24/element-ui-focus-js和migrating.js文件源碼學習