Element-UI radio、radio-group、radio-button 單選框原始碼

禿如其來的禿頭發表於2020-11-06

Element-UI radio 單選框原始碼

radio模板部分:
<template>
  <label
  //label 元素不會向使用者呈現任何特殊效果。不過,它為滑鼠使用者改進了可用性。如果您在 label 元素內點選文字,就會觸發此控制元件。就是說,當使用者選擇該標籤時,瀏覽器就會自動將焦點轉到和標籤相關的表單控制元件上。
    class="el-radio"
    :class="[
      border && radioSize ? 'el-radio--' + radioSize : '', //border 與 radioSize 同時存在則有該樣式
      { 'is-disabled': isDisabled }, 	//是否禁用
      { 'is-focus': focus },			//是否選中
      { 'is-bordered': border },		//是否有邊框
      { 'is-checked': model === label } // model計算屬性見下面 script 
    ]"
    role="radio"	// role aria-checked aria-disabled 用於螢幕閱讀器的,幫助殘障人士更好的訪問網站
    :aria-checked="model === label"
    :aria-disabled="isDisabled"
    :tabindex="tabIndex"  // 設定是否可以通過鍵盤上的 tab鍵 進行選擇,  -1 代表不可選, 0 代表可選
    @keydown.space.stop.prevent="model = isDisabled ? model : label" 
    // keydown.space 空格  .stop 停止冒泡  .prevent 阻止預設行為   .stop.prevent 串聯修飾符
  >
    <span class="el-radio__input"
      :class="{
        'is-disabled': isDisabled,
        'is-checked': model === label
      }"
    >
      <span class="el-radio__inner"></span> // 採用 css 偽類實現選中效果,代替 input 
      <input
        ref="radio"
        class="el-radio__original" //設定了 opacity: 0 對其隱藏
        :value="label"
        type="radio"
        aria-hidden="true"
        v-model="model"
        @focus="focus = true"
        @blur="focus = false"
        @change="handleChange"
        :name="name"
        :disabled="isDisabled"
        tabindex="-1"
      >
    </span>
    <span class="el-radio__label" @keydown.stop>
      <slot></slot>
        // 要是沒有插槽內容則顯示 label
      <template v-if="!$slots.default">{{label}}</template>
    </span>
  </label>
</template>

問題一:為啥不用原生 input 而是自己模擬

1、原生標籤 radio不同瀏覽器樣式不同,所以自己寫來代替

2、需要用到原生的 radio來獲取焦點來觸發 change事件,所以 element是 採用了 絕對定位脫離文件流,以及設定透明度為0,而不是採用 dispaly:none或者 visibility:hidden

radioscript 部分:
export default {
    name: 'ElRadio', // name 三個好處 見其他原始碼文章

    mixins: [Emitter], // mixins 下文單獨拎出來與之相關部分  也就是 自己實現 dispatch

    inject: {	//與 Form 表單相關
      elForm: {
        default: ''
      },

      elFormItem: {
        default: ''
      }
    },

    componentName: 'ElRadio', //用於當前 Vue 例項的初始化選項。需要在選項中包含自定義 property 時會有用處:
	/* 比如:
		new Vue({
          customOption: 'foo',
          created: function () {
            console.log(this.$options.customOption) // => 'foo'
          }
		})
	*/ 
    
    
    
    props: {	// 屬性傳值
      value: {},
      label: {},
      disabled: Boolean,
      name: String,
      border: Boolean,
      size: String
    },

    data() {
      return {
        focus: false
      };
    },
    computed: {
      isGroup() { // 是否為 radio-group 元件例項 ,是則為 true , 否則為 false
        let parent = this.$parent; 
        // 向上迴圈找它爸爸,一直找到為止 也就是 下面的 parent.$options.componentName === 'ElRadioGroup' 的時候
        while (parent) {
          if (parent.$options.componentName !== 'ElRadioGroup') {
            parent = parent.$parent;
          } else {
            this._radioGroup = parent; // 父例項,如果當前例項有的話
            return true;		
          }
        }
        return false;
      },
      model: {
        get() {
          return this.isGroup ? this._radioGroup.value : this.value;
        },
        set(val) {
          if (this.isGroup) {
            this.dispatch('ElRadioGroup', 'input', [val]);
          } else {
            this.$emit('input', val);
          }
          this.$refs.radio && (this.$refs.radio.checked = this.model === this.label);
        }
      },
      _elFormItemSize() {
        return (this.elFormItem || {}).elFormItemSize;
      },
      radioSize() {
        const temRadioSize = this.size || this._elFormItemSize || (this.$ELEMENT || {}).size;
        return this.isGroup
          ? this._radioGroup.radioGroupSize || temRadioSize
          : temRadioSize;
      },
      isDisabled() {
        return this.isGroup
          ? this._radioGroup.disabled || this.disabled || (this.elForm || {}).disabled
          : this.disabled || (this.elForm || {}).disabled;
      },
      tabIndex() {
        return (this.isDisabled || (this.isGroup && this.model !== this.label)) ? -1 : 0;
      }
    },

    methods: {
      handleChange() {
        this.$nextTick(() => {
          this.$emit('change', this.model);
          this.isGroup && this.dispatch('ElRadioGroup', 'handleChange', this.model);
        });
      }
    }
  };


相關文章