Element分析(元件篇)——Radio

weixin_34249678發表於2017-01-01

_index.js

單選框內部一共有三個元件:el-radioel-radio-buttonel-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

然後,是作為inputspan部分,它包含一個用來表示選中效果的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

labelradio選中時的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-buttonradio基本上一樣。

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-buttonradio不同,它不能脫離radio-group單獨使用:

computed: {
  value: {
    get() {
      return this._radioGroup.value;
    },
    set(value) {
      this._radioGroup.$emit('input', value);
    }
  },
}

input

然後是用來模擬原生radioinput,上面繫結了相應的資料:

<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
    };
  },
}

相關文章