1. 程式人生 > 其它 >模仿element-ui封裝vue元件庫(radio,radio-group)

模仿element-ui封裝vue元件庫(radio,radio-group)

七、封裝一個element-ui風格的radio元件

前置知識點:

radio的基本使用
引數支援:

引數名稱  引數描述 引數型別 預設值
v-model 雙向繫結 boolean false
label 單選框和value值 string,num,boolean ''
name name屬性 string ''

7.1radio元件的基本框架和樣式

框架、基本樣式以及選中樣式:

<template>
<label class="ra-radio is-checke">
<span class="ra-radio_input">
<
span class="ra-radio_inner"></span> <input type="radio" class="ra-radio_original" > </span> <span class="ra-radio_label">我是label</span> </label> </template> <script> export default { name: 'ra-radio', props: {} }, watch: {}, data () { return {} }, methods: {} }
</script> <style lang="scss" scoped> .ra-radio{ color: #606266; font-weight: 500; line-height: 1; position: relative; cursor: pointer; display: inline-block; white-space: nowrap; outline: none; font-size: 14px; margin-right: 30px; -moz-user-select: none; -webkit-user-select: none; -moz-user-select
: none; .one-radio_input{ white-space: nowrap; cursor: pointer; outline: none; display: inline-block; line-height: 1; position: relative; vertical-align: middle; .ra-radio_inner{ border: 1px solid #dcdfe6; border-radius: 100%; width: 14px; height: 14px; background-color: #fff; position: relative; cursor: pointer; display: inline-block; box-sizing: border-box; &:after{ width: 4px; height: 4px; border-radius: 100%; background-color: #fff; content: ""; position: absolute; left: 50%; top: 50%; transform: translate(-50%,-50%) scale(0); transition: transform .15s ease-in; } } .ra-radio_original{ opacity: 0; outline: none; position: absolute; z-index: -1; top: 0; left: 0px; right: 0; bottom: 0; margin: 0; } } .ra-radio_label{ font-size: 14px; padding-left: 10px;; } } // 選中的樣式 .ra-radio.is-checked{ .ra-radio_input{ .ra-radio_inner{ border-color: #409eff; background-color: #409eff; &:after{ transform: translate(-50%,-50%) scale(1); } } } .ra-radio_label{ color:#409eff; } } </style>

7.2radio元件的資料雙向繫結

實現radio元件的資料雙向繫結,除了要繫結資料本身外,還要控制radio元件的樣式。

實現radio元件資料的繫結,需要父元件傳遞的label值和value值,其中value值使用v-model語法糖來繫結。

<ra-radio v-model="gender" label="0"></ra-radio>
<ra-radio v-model="gender" label="1"></ra-radio>

子元件接收資料後,要對資料進行處理。

當radio元件被點選時,繫結的資料應該變為該元件的label值。我們將元件中的input標籤的value繫結為傳入的label值,並且宣告一個計算屬性model雙向繫結到input元件上,model我們需要通過get方法獲取值;並且通過set方法將值回撥給父元件。

同時,當我們在點選radio元件時,我們應該讓被選中的元件新增選中樣式,我們通過label和value的比較來判斷,如果相同則顯示選中樣式。

<template>
<label class="ra-radio" :class="{'is-checked': label == value}">
<span class="ra-radio_input">
<span class="ra-radio_inner"></span>
<input
type="radio"
class="ra-radio_original"
:value="label"
v-model="model"
>
</span>
<span class="ra-radio_label">
<slot></slot>
<!-- 如果沒有傳值,就把label作為文字顯示 -->
<template v-if="!$slots.default">{{label}}</template>
</span>
</label>
</template>
//計算屬性
computed: {
model: {
get () {
return this.value
},
set (value) {
// 觸發父元件的input事件
this.$emit('input', value)
}
}
},

八、封裝一個element-ui風格的radio-group元件

radio-group元件是再radio元件上進行優化的,它的目的是在我們使用radio元件時,不必給每個元件都新增一個v-model,而是通過繫結一個v-model來實現資料繫結。

使用radio-group元件包裹radio元件時,需要考慮到的一個問題就是radio-group元件於radio元件之間的通訊。我們在使用radio-group元件時將資料通過v-model進行了繫結,那麼raido元件就不能直接拿到這個值,所以我們需要使用provide/inject進行祖孫元件之間得傳值。

使用provide/inject非常簡單,在radio-group中通過宣告provide物件將元件自身進行傳遞,在radio中使用inject進行接收即可。

radio-group元件架構:

<template>
<div class="ra-radio-group">
<slot></slot>
</div>
</template>
<script>
export default {
name: 'ra-radio-group',
provide () {
  return {
    RadioGroup: this
  }
},
props: {
// 元件接收到了value值,我們需要觸發這個元件的input事件
// provide 與 inject 用來做祖孫之間得元件通訊
value: null
}
}
</script>

在radio元件中,通過inject可以直接接收到引數,此時,原本通過v-model傳遞進來的value值,變成了radio-group元件傳進來的RadioGroup.value值,所以在computed計算屬性中,我們先寫一個radio元件是否被radio-group元件進行判斷的方法,並且使用在model中,如果被包裹了,則使用RadioGroup.value值,否則使用value值。

同時在is-checked類的判斷上拋棄label於value的比較,轉而通過label於model(model此時的值為value或RadioGroup.value)比較,來進行樣式的更改。

<template>
<label class="ra-radio" :class="{'is-checked': label == model}">
<span class="ra-radio_input">
<span class="ra-radio_inner"></span>
<input
type="radio"
class="ra-radio_original"
:value="label"
v-model="model"
>
</span>
<span class="ra-radio_label">
<slot></slot>
<!-- 如果沒有傳值,就把label作為文字顯示 -->
<template v-if="!$slots.default">{{label}}</template>
</span>
</label>
</template>
<script>
export default {
name: 'ra-radio',
props: {
label: {
type: [String, Number, Boolean],
defualt: ''
},
value: null,
name: {
type: String,
defualt: ''
}
},
inject: {
RadioGroup: {
default: ''
}
},
computed: {
model: {
get () {
return this.isGroup ? this.RadioGroup.value : this.value
},
set (value) {
// 觸發父元件的input事件
this.isGroup ? this.RadioGroup.$emit('input', value) : this.$emit('input', value)
}
},
// 用於判斷radio是否被radioGroup包裹
isGroup () {
return !!this.RadioGroup
}
}
}
</script>


--------------------------------------------至此,radio元件與radio-group元件封裝完畢--------------------------------------------------

附radio元件程式碼:

<template>
  <label class="ra-radio" :class="{'is-checked': label == model}">
    <span class="ra-radio_input">
      <span class="ra-radio_inner"></span>
      <input
      :value="label"
      :name="name"
      v-model="model"
      type="radio"
      class="ra-radio_original"
      >
    </span>
    <span class="one-radio_label">
      <slot></slot>
      <template v-if="!$slots.default">
        {{label}}
      </template>
    </span>
  </label>
</template>

<script>

export default {
  name: 'ra-radio',
  inject: {
    RadioGroup: {
      default: ''
    }
  },
  data () {
    return {
    }
  },
  props: {
    label: {
      type: [String, Number, Boolean],
      default: ''
    },
    value: null,
    name: {
      type: String,
      default: ''
    }
  },
  computed: {
    model: {
      get () {
        return this.isGroup ? this.RadioGroup.value : this.value
      },
      set (value) {
        // 觸發父元件的input事件
        this.isGroup ? this.RadioGroup.$emit('input', value) : this.$emit('input', value)
      }
    },
    // 用於判斷radio是否被radioGroup包裹
    isGroup () {
      return !!this.RadioGroup
    }
  }
}
</script>
<style lang="scss" scoped>
.ra-radio{
    color: #606266;
    font-weight: 500;
    line-height: 1;
    position: relative;
    cursor: pointer;
    display: inline-block;
    white-space: nowrap;
    outline: none;
    font-size: 14px;
    margin-right: 30px;
    -moz-user-select: none;
    -webkit-user-select: none;
    -moz-user-select: none;
    .ra-radio_input{
      white-space: nowrap;
      cursor: pointer;
      outline: none;
      display: inline-block;
      line-height: 1;
      position: relative;
      vertical-align: middle;
      .ra-radio_inner{
        border: 1px solid #dcdfe6;
        border-radius: 100%;
        width: 14px;
        height: 14px;
        background-color: #fff;
        position: relative;
        cursor: pointer;
        display: inline-block;
        box-sizing: border-box;
        &:after{
          width: 4px;
          height: 4px;
          border-radius: 100%;
          background-color: #fff;
          content: "";
          position: absolute;
          left: 50%;
          top: 50%;
          transform: translate(-50%,-50%) scale(0);
          transition: transform .15s ease-in;
        }
      }
      .ra-radio_original{
        opacity: 0;
        outline: none;
        position: absolute;
        z-index: -1;
        top: 0;
        left: 0px;
        right: 0;
        bottom: 0;
        margin: 0;
      }
    }
    .ra-radio_label{
      font-size: 14px;
      padding-left: 10px;;
    }
  }
  // 選中的樣式
  .ra-radio.is-checked{
    .ra-radio_input{
      .ra-radio_inner{
        border-color: #409eff;
        background-color: #409eff;
        &:after{
          transform: translate(-50%,-50%) scale(1);
        }
      }
    }
    .ra-radio_label{
      color:#409eff;
    }
  }
</style>

附radio-group元件程式碼:

<template>
  <div class="ra-radio-group">
    <slot></slot>
  </div>
</template>

<script>

export default {
  name: 'ra-radio-group',
  provide () {
    return {
      RadioGroup: this
    }
  },
  props: {
    // 元件接收到了value值,我們需要觸發這個元件的input事件
    // provide 與 inject  用來做祖孫之間得元件通訊
    value: null
  }
}
</script>
<style lang="scss" scoped>
</style>