1. 程式人生 > >element-ui switch元件原始碼分析整理筆記(二)

element-ui switch元件原始碼分析整理筆記(二)

原始碼如下:

<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檔案原始碼學習