仿照Element-ui實現(xiàn)一個簡易的$message方法
前言
在需要對用戶進行提示時,有時會遇到這種場景:使用模態(tài)框提示太過硬核,使用toast提示又太輕了,這時候可以選擇使用頁面頂部滑下的消息提示。本文仿照element-ui的實現(xiàn)一個類似$message的方法。
思路梳理
首先我們來看下element-ui中消息提示的效果是怎么樣的,找些思路。
從圖中我們可以看消息提示是可以同時顯示多條的,并且定位看起來都是fixed,居中展示,我們自然可以想到使用數(shù)組來存儲這些消息的信息,并且根據(jù)每一條提示消息的顯示隱藏更改每一項的top值,然后就是加一些動畫(使用transition)以及細節(jié)處理了。
組件編寫
新建兩個組件MsgBox.vue和Msg.vue,前者負責(zé)收集和處理傳入的消息數(shù)據(jù)(如:{type: 'success', message: '提示消息'}),對數(shù)組進行一定處理后,再將每一項傳給Msg.vue展示。
MsgBox組件
ts部分
我們首先在MsgBox.vue中編寫方法處理數(shù)組的方法addMsg、resetTop和clear,其中addMsg負責(zé)收集消息數(shù)據(jù),給每一個msg添加一個負責(zé)控制該條消息顯示隱藏的屬性show;resetTop負責(zé)控制消息距頂距離的屬性top及各條消息的顯示隱藏;clear負責(zé)當(dāng)數(shù)組中所有消息都處于隱藏狀態(tài)時將消息數(shù)組清空:
private addMsg(msg: Msg) { this.msgs.push({...msg, show: true}) this.resetTop() } private resetTop(ind1 = -1) { this.clear() let ind = 0 const msgs = this.msgs.map((msg: MsgInfo, i: number) => { if (i === ind1) { msg.show = false } if (msg.show) { msg.top = 20 + ind * 72 ind++ } return msg }) this.msgs = [...msgs] } private clear() { clearTimeout(this.timer) this.timer = setTimeout(() => { const allFalse = this.msgs.some((t) => t.show) if (!allFalse) { this.msgs = [] } }, 1000) }
每次有新消息加入,或者原有消息隱藏時都會觸發(fā)resetTop方法,用來重新計算各條消息的位置。
template部分
html部分就比較簡單了,只是遍歷msgs數(shù)組,將每一項傳給子組件Msg。
<template> <div> <msg-box v-for="(msg,i) of msgs" :msg="msg" :key="i" :ind="i" @resetTop="resetTop" :msgs="msgs"></msg-box> </div> </template>
這里傳入數(shù)組msgs的原因是在每次調(diào)用resetTop更改數(shù)組時,子組件監(jiān)聽不到msg發(fā)生的變化,只好將msgs傳入,直接從msgs中取相關(guān)數(shù)據(jù),如果哪位大佬看出問題了希望可以指點下。
Msg組件
ts部分
子組件中邏輯較少,主要是在組件掛載時啟動一個定時器,在一定時間后通過emit觸發(fā)父組件中的resetTop方法將組件關(guān)閉。 另外還有一些根據(jù)參數(shù)獲取當(dāng)前消息信息的computed方法。
private get info() { const msgs = this.msgs as MsgInfo[] return msgs[this.ind] } private get boxClass() { const type = this.msg.type return type ? `box-item-${type}` : '' } @Emit('resetTop') private close() { return this.ind } private mounted() { if (this.msg.delay !== 0) { const delay = parseInt(this.msg.delay) || 3000 setTimeout(() => { this.close() }, delay) } }
template部分
視圖部分也比較簡單,主要是使用了vue自帶的transition組件實現(xiàn)的動畫效果,注意要加上appear屬性才有入場動畫效果。
<template> <transition name="msg" appear> <div :class="['box-item', boxClass]" v-if="info.show" :style="{top: info.top + 'px'}"> <div class="msg-container"> <i :class="['iconfont', iconClass]"></i> {{ info.msg }} </div> <span @click="close"> <i class="iconfont icon-cc-close"></i> </span> </div> </transition> </template>
css部分
樣式部分主要是借鑒了element-ui的樣式,以及使用了animation做了簡單的動畫效果
.box-item { height: 16px; position: fixed; min-width: 380px; // element-ui抄來的樣式 border-width: 1px; border-style: solid; border-color: #EBEEF5; position: fixed; left: 50%; transform: translateX(-50%); background-color: #edf2fc; transition: opacity .3s,transform .4s,top .4s; padding: 15px 15px 15px 20px; display: flex; align-items: center; justify-content: space-between; &-success{ background-color: #f0f9eb; border-color: #e1f3d8; } &-warning { background-color: #fdf6ec; border-color: #faecd8; } &-error { background-color: #fef0f0; border-color: #fde2e2; } } .msg-container { display: flex; align-items: center; .iconfont { margin-right: 5px; } } .msg-enter-active { animation: anim 0.5s; } .msg-leave-active { animation: anim 0.5s reverse; } @keyframes anim { 0% { opacity: 0; transform: translate(-50%, -200%); } 100% { opacity: 1; transform: translate(-50%, 0); } }
到此為止,除css代碼外不到150行實現(xiàn)了消息提示組件。
將組件中方法放到Vue原型鏈上
但是我們該怎么調(diào)用呢,參考element-ui中的使用方式this.$message,是把組件的入口方法掛載到Vue的原型鏈上,并且在此之前應(yīng)該實例化了該組件,接下來我們就要實例化組件,然后將組件實例掛載到body上,并且將實例上的入口方法加在Vue原型鏈上。
這里使用到了我們公司一位大佬參考react寫的vue中的傳送門方法portal,主要思路是將組件掛載新的Vue上,并實例化,然后再將新實例掛載到body下面(這樣也防止DOM層級嵌套產(chǎn)生的zIndex無法覆蓋等問題),最后將指定方法添加到Vue原型鏈上。
import Vue, {VueConstructor} from "vue"; interface Param { cmp: VueConstructor & {instance?: () => any}; name: string; method?: string; target?: string | HTMLElement; props?: any; } export default function portal(param: Param){ let {cmp, name, method, target = document.body, props = {}} = param if(typeof target === 'string') target = document.querySelector(target) as HTMLElement; method = method || 'show' cmp.instance = ()=>{ let instance = new Vue({ render(h){ return h(cmp, {props}) } }) instance.$mount(); // 將instance.$el放到想放的位置 (target as HTMLElement).appendChild(instance.$el); return instance.$children[0]; } const instance = cmp.instance() Vue.prototype[`$${name}`] = instance[method]; }
接著,在Vue項目入口文件中使用傳送門方法將Msg組件掛載上去就可以在組件中使用了。
portal({ name: 'msg', cmp: MsgBox, method: 'addMsg' })
到此這篇關(guān)于仿照Element-ui實現(xiàn)一個簡易的$message方法的文章就介紹到這了,更多相關(guān)Element-ui $message內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue實現(xiàn)盒子內(nèi)拖動方塊移動的示例代碼
這篇文章主要給大家介紹了如何通過vue實現(xiàn)盒子內(nèi)拖動方塊移動,文章通過代碼示例講解的非常詳細,具有一定的參考價值,感興趣的小伙伴可以參考閱讀本文2023-08-08vue+iview/elementUi實現(xiàn)城市多選
這篇文章主要介紹了vue+iview/elementUi實現(xiàn)城市多選,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-03-03基于element-ui封裝可搜索的懶加載tree組件的實現(xiàn)
這篇文章主要介紹了基于element-ui封裝可搜索的懶加載tree組件的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-05-05vue插件vue-resource的使用筆記(小結(jié))
本篇文章主要介紹了vue插件vue-resource的使用筆記(小結(jié)),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-08-08vue-router解決相同路徑跳轉(zhuǎn)報錯的問題
這篇文章主要介紹了vue-router解決相同路徑跳轉(zhuǎn)報錯的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-04-04vue-video-player 解決微信自動全屏播放問題(橫豎屏導(dǎo)致樣式錯亂問題)
這篇文章主要介紹了vue-video-player 解決微信自動全屏播放問題,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-02-02