elementui原始碼學習之仿寫一個el-message

語言: CN / TW / HK

問題描述

工作中雖然使用工具庫很快樂很高效,但我們還是要抽空看看工具庫的原始碼,因為原始碼中會用到一些不經常使用的api方法,記住這些api方法,可提升自己的程式設計能力,有助於以後封裝自己的工具庫,從而更好的實現一些需求。

需求分析

元件封裝之前,我們要想一下要封裝的這個元件的應用場景和使用需求有哪些,以此為突破口,便於更好的實現程式碼邏輯

應用場景和需求:訊息提示

愚以為,message主要是資訊提示,應用場景在於使用者執行了一些操作,是否成功或失敗之類的互動反饋。所以,我們可以定義這個要封裝的元件有以下需求:

  • 需要可以輸入資訊文字 message引數
  • 需要message資訊的型別反饋(成功反饋、警告反饋、錯誤反饋、普通訊息反饋)type引數
  • 需要提示完以後,可以設定預設消失時間 duration引數
  • 當滑鼠懸浮的時候保留這個訊息提示,不讓其消失 定時器timer引數
  • 其他諸如 提示小圖示的型別和文字是否居中 之類的

如果我們看餓了麼UI官方的元件,我們會發現官方考慮的還是十分詳細的,給到了很多配置項Options引數,不過在我們自己實際封裝元件中,不需要像官方那樣,做很多的配置項,只需要實現常用的配置項即可,保留最實用的功能即可。

餓了麼UI官方el-message元件:http://element.eleme.cn/#/zh-CN/component/message

效果圖

1.gif

理解方式

關於這個功能效果,個人建議可以如下理解 - 先複習一下平常用的不多的api - 再把程式碼clone下來,跑起來(文末附上github程式碼倉庫地址) - 結合註釋,既可快速理解了

知識點複習之:class的陣列用法和:style用法

``js // html <div :class="[ 'messageBox', /* .messageBox這個類名確定要加到div這個標籤上 */ center ? 'horizontal' : '', /* 是否給div標籤加上.horizontal這個類名取決於center這個變數的值是否為true */ typeArr.includes(type) ? type : '', /* 是否給div這個標籤加上type變數值的類名,取決於typeArr變數陣列是否包含type的值 */ ]" :style="controlTop" /* 等價於:style={top:12px`} 等價於 style="top: 12px" 即距離頂部top值為12畫素 */

// js data() { return { center: false, // 是否讓水平文字居中,預設false type: "info", // 預設info型別 typeArr: ["info", "success", "warning", "error"], // 總共4種類型 }; }, computed: { controlTop() { return { top: 12px }; }, }, ``` 為什麼要提到:class的陣列用法以及:style的用法呢?

因為在本例中,給message繫結四種類型(成功、警告、錯誤、資訊)的樣式,就需要使用到;

使用者多次點選觸發message的出現,控制下一個message的位置在上一個的下方,就需要讓不斷的更改下一個message的top值;

知識點複習之transition過渡鉤子函式

```js // html <transition v-on:before-enter="beforeEnter" / 過渡出現進入之前 / v-on:enter="enter" / 過渡出現進入 / v-on:after-enter="afterEnter" / 過渡出現進入之後 / v-on:enter-cancelled="enterCancelled" / 取消進入過渡 / v-on:before-leave="beforeLeave" / 過渡消失離開之前 / v-on:leave="leave" / 過渡消失離開 / v-on:after-leave="afterLeave" / 過渡消失離開之後 / v-on:leave-cancelled="leaveCancelled" / 取消過渡消失離開 /

<!-- ... -->

// js methods: { // ... afterLeave(){ / 本例中使用了這個鉤子,當過渡消失的時候會觸發這個鉤子函式, 我們可以在鉤子函式中寫一些js邏輯程式碼,進行相應操作 / } // ...

} ``` 那麼,什麼時候,過渡消失,什麼時候過渡出現?

最明顯的就是,v-show由true改為false、由false改為true的時候,會自動觸發transition的過渡鉤子函式執行

本例中使用過渡鉤子函式主要是因為,當消失一個message的時候,需要減少一個message的計數,所以需要通過這個鉤子去進行js邏輯程式碼曹組歐

過渡鉤子詳見官方文件:http://cn.vuejs.org/v2/guide/transitions.html

知識點複習之vue銷燬元件的方式

  1. 使用v-if官方推薦最好的方式
  2. 使用key 一般用的不是特別多
  3. this.$destroy(true) vue的1.x版本常常使用,從2.x版本就不支援了,相當於相容寫法
  4. 自己手動移除this.$el.parentNode.removeChild(this.$el); 本例使用之 關於第3點和第4點,尤大佬還親自回答了關於這個問題的issues。簡單截圖如下:

snipaste_20220710_144810.png

issues地址如下:http://github.com/vuejs/vue/issues/3534

為什麼提到這個銷燬dom方式呢?

因為我們使用v-show加上去transition控制message的隱藏和消失的,這個效果絲滑一些,沒有使用v-if直接幹掉dom。所以需要手動寫程式碼,在過渡消失以後,當我們看不到message的時候,再偷偷的給message移除掉即可

完整程式碼

整體程式碼思路

  1. 搞一個message元件用於繼承
  2. 使用Vue.extend繼承這個元件形成一個構造器
  3. 定義一個函式,函式一執行,就使用構造器建立一個message顯示,預設3秒自動消失
  4. 把這個函式掛載在原型上,並暴露出去,方便訪問使用

關於Vue.extend繼承不太熟悉的,可以先看看筆者的另外兩篇文章哦

繼承... http://juejin.cn/post/7021724333391216677

繼承... http://juejin.cn/post/7108542695387168799

使用的.vue檔案程式碼

```js // html

// 一種是原型鏈使用方式,另一種是引入使用方式 import MyMessage from "@/components/index.js"; methods: { showMessage1() { this.$myMessage({ message: "資訊彈出", type: "info", }); }, showMessage2() { this.$myMessage({ message: "成功彈出", type: "success", }); }, showMessage3() { this.$myMessage({ message: "警告彈出", type: "warning", }); }, showMessage4() { this.$myMessage({ message: "錯誤彈出", type: "error", }); }, showMessage5() { this.$myMessage({ message: "彈出5秒關閉", }); }, showMessage6() { this.$myMessage({ message: "文字居中哦", center: true, }); }, showMessage7() { MyMessage({ message: "引入使用", type: "success", }); }, }, ```

通過繼承掛載原型上便於動態建立檔案程式碼

```js import Vue from 'vue'; import messageComponent from './src/index.vue' // 引入元件,方便繼承 let MessageConstructor = Vue.extend(messageComponent); // 引入一個message構造器,方便new之

let instance = null // 定義元件例項 let count = 0 // 定義統計次數,便於知道建立多少個例項

const MyMessage = function (options) { if (options.duration & typeof options.duration !== 'number') { // 對於duration數字型別的校驗 console.error('Error! duration Must be a numeric type ') // 使用者亂傳遞非數字型別引數,就拋錯不執行後續程式碼 return } count = count + 1 // MyMessage函式呼叫一次,統計次數加一個 instance = new MessageConstructor({ // 例項化一個元件例項 data: options, // data傳引數,元件的data接收(即傳遞配置項) propsData: { // propsData傳參, count: count, // 將統計的次數傳遞給子元件 cutCount: cutCount // 傳遞一個函式,當MyMessage消失的時候,通知外界 }, }); instance.$mount(); // 例項元件掛載 document.body.appendChild(instance.$el); // 把這個元件例項的dom元素,追加到document文件中 instance.isShowMyMessage = true; // 將元件的isShowMyMessage屬性值置為true,即讓例項出現,即訊息出現 return instance; // MyMessage函式執行一次,就會返回一個加工好的例項物件 }

function cutCount() { // 當message消失一個 count = count - 1 // 就把外界統計的數量減少一個 let messageBoxDomList = document.querySelectorAll('.messageBox') // 然後選中所有的messageDOM元素 for (let i = 0; i < messageBoxDomList.length; i++) { // 遍歷一下這個DOM偽陣列 let dom = messageBoxDomList[i] // 所有的都往上移動60畫素 dom.style['top'] = parseInt(dom.style['top']) - 60 + 'px' } }

export default MyMessage // 暴露出去 Vue.prototype.$myMessage = MyMessage; // 掛載在vue原型上,方便this.$myMessage呼叫 ```

用於繼承的message元件程式碼

```js

```

github倉庫程式碼地址

elementui原始碼學習仿寫元件,準備工作不忙的時候,寫一個系列,我會盡可能多寫點註釋哦。與大家共同進步成長^_^

github地址:http://github.com/shuirongshuifu/elementSrcCodeStudy