elementui原始碼學習之仿寫一個el-badge標記

水冗水孚發表於2022-07-17
本篇文章記錄一下,仿寫一個el-badge標記提示,有助於大家更好理解,餓了麼ui的輪子具體工作細節。本文是elementui原始碼學習仿寫系列的又一篇文章,後續空閒了會不斷更新並仿寫其他元件。原始碼在github上,大家可以拉下來,npm start執行跑起來,有助於更好的理解。github倉庫地址如下:https://github.com/shuirongsh...

知識點複習

還是像往常一樣,為了便於更好的理解程式碼,我們們先複習一下,el-badge元件中使用的一些曝光度不是非常高的知識點api

sup和sub標籤

  • sup標籤可定義文字上標內容(如平方、立方、未讀訊息等...)
  • sub標籤可定義文字下標內容(一般是科學公式多一些)

程式碼舉例:

<h5>未讀訊息<sup style="color:red">99+</sup></h5>
<h5>水是生命之源,化學分子式為:H<sub style="color:brown">2</sub>O</h5>

效果圖:

由此我們可以明瞭,sup標籤內寫的文字,會自動放在右上方,也可以正常設定樣式,所以使用sup標籤用來做el-badge標記元件,的確是比較合適的。

不過因為需要使用插槽傳入定義內容,所以餓了麼UI中使用sup標籤不像上述巢狀的,相當於並排的,如:<h5>未讀訊息</h5><sup style="color:red">99+</sup> 所以餓了麼又通過樣式定位修改其位置,使其在右上角了

props中的validator校驗函式

假設需求:父元件傳遞給子元件的age屬性欄位的值需要為數字型別的

我們一般都會這樣寫:

props: {
    age: Number,
},

或者這樣寫(額外指定一個預設值):

props: {
  age: {
    type: Number,
    default: 100,
  }
},

這樣寫一般也就夠用了,不過如果要精細化校驗,就需要使用props校驗某個屬性欄位自帶的validator函式了。

比如新需求:父傳子的age屬性欄位需為數字型別、且不能超過180

於是乎,我們使用validator函式可以這樣寫:

props: {
    age: {
      type: Number,
      default: 100,
      validator(val) { // validator函式接收的引數是父傳子此欄位的值
        if (val <= 180) {
          return true; // 返回true代表校驗通過,不報[Vue warn]警告
        } else {
          return false; // 返回false代表校驗不通過,報[Vue warn]警告,告知使用者傳值不對
        }
      },
    },
}

這樣的話,的確是更方便精細化控制父傳子引數了。官方說明:https://cn.vuejs.org/v2/guide...

仿寫效果圖

使用程式碼

<template>
  <div class="wrap">
    <my-badge :value="11" type="primary">
      <h5>primary型別</h5>
    </my-badge>
    <br />
    <br />
    <my-badge :value="22" type="success">
      <h5>success型別</h5>
    </my-badge>
    <br />
    <br />
    <my-badge value="0" type="warning">
      <h5>warning型別</h5>
    </my-badge>
    <br />
    <br />
    <my-badge :value="44" type="info">
      <h5>info型別</h5>
    </my-badge>
    <br />
    <br />
    <my-badge :value="55" type="danger">
      <h5>danger型別</h5>
    </my-badge>
    <br />
    <br />
    <my-badge :value="188" :max="99">
      <h5>指定max最大值99</h5>
    </my-badge>
    <br />
    <br />
    <my-badge is-dot>
      <h5>小圓點</h5>
    </my-badge>
    <br />
    <br />
    <my-badge value="熱點追蹤">
      <h5>《震驚!一程式猿光天化日竟然...網友必看!》</h5>
    </my-badge>
    <br />
    <br />
    <my-badge :isShow="isShow" value="^@^">
      <h5>隱藏</h5>
    </my-badge>
    <br /><button @click="isShow = !isShow">隱藏顯示切換</button>
  </div>
</template>

<script>
export default {
  data() { return { isShow: true } },
};
</script>

<style lang="less" scoped> .wrap { width: 100%; height: 100%; padding: 24px;} </style>

仿寫封裝程式碼

<template>
  <div class="my-badge">
    <slot></slot>
    <!-- 使用sup上標,讓sup標籤內的文字內容渲染在的右上角(需要巢狀),但是
    這裡的寫法不會讓sup標籤的文字直接渲染到右上角,還需定位移動控制一下樣式 -->
    <transition name="el-zoom-in-center">
      <!-- el-zoom-in-center過渡動畫是自帶的哦,我們們直接加上就能用啦 -->
      <sup
        v-show="showSup"
        :class="[
          'fixedRightTop',
          typeArr.includes(type) ? type : '',  /** 型別陣列是否包含傳進來的型別,包含就應用此型別樣式,不包含就不應用型別樣式 */
          isDot ? 'isDotClass' : '',  /** isDot為true就加上小圓點樣式型別 */
        ]"
        v-text="showValue"
      ></sup>
    </transition>
  </div>
</template>

<script>
// 定義這五種型別
const typeArr = ["primary", "success", "warning", "info", "danger"];
export default {
  name: "myBadge",
  props: {
    value: [String, Number],
    type: {
      type: String,
      validator(val) {
        // console.log("校驗引數", val);
        // props也可以加入型別校驗函式,vue自帶的哦,不符合就丟擲警告
        if (typeArr.includes(val)) {
          return true;
        } else {
          return false;
        }
      },
    },
    max: {
      type: Number,
      validator(val) { // 校驗最大值要是數字
        if (val) {
          if (typeof val === "number") {
            return true;
          } else {
            return false;
          }
        } else {
          return true;
        }
      },
    },
    isDot: Boolean,
    isShow: {
      // 預設展示
      type: Boolean,
      default: true,
    },
  },
  data() { return { typeArr } },
  computed: {
    showValue() {
      // 如果是小圓點,就不用返回值
      if (this.isDot) {
        return;
      }
      // 如果告知最大值,就做一個判斷
      if (this.max) {
        return this.value > this.max ? `${this.max}+` : this.value;
      }
      // 其他的情況,無論是數字或者自定義文字,就正常顯示即可
      else {
        return this.value;
      }
    },
    showSup() {
      // isShow為false就隱藏嘍
      if (!this.isShow) {
        return false;
      }
      // 內容為空,也隱藏咯
      if (this.value === "") {
        return false;
      }
      // 其他正常顯示
      return true;
    },
  },
};
</script>

<style lang="less" scoped>
.my-badge {
  position: relative;
  vertical-align: middle;
  display: inline-block; // 把塊元素轉換成行內塊元素,如此,寬度便由內容撐開了
  // 通過定位,把上標定位到右上角的哦
  .fixedRightTop {
    position: absolute; // 因為上方display: inline-block;了
    top: 0; // 所以定位才正好在插槽內容的右上方了,否則塊元素預設寬度100%,那定位就在最右側了
    right: 3px; // 但是還需要transform平移一下
  }
  sup {
    color: #fff;
    background-color: #f56c6c; // 預設淡紅色
    border-radius: 10px; // 加個圓角好看些
    padding: 1px 4px;
    font-size: 12px; // 上標字型要設定最小
    transform: translate(100%, -50%); // 要移動一下才行
    white-space: nowrap; // 自定義文字內容會換行,通過css控制,使之不換行
  }
  // 五種型別配色,這裡直接抄官方的
  .primary { background-color: #409eff; }
  .success { background-color: #67c23a; }
  .warning { background-color: #e6a23c; }
  .info { background-color: #909399; }
  .danger { background-color: #f56c6c; }
  // 小圓點加樣式
  .isDotClass { height: 8px; width: 8px; right: 1px; border-radius: 50%; }
}
</style>

總結

好了,一篇water文章寫完啦(手動捂臉哭),不過愚以為不斷學習、不斷輸出、大抵便是不斷進步吧

相關文章