Vue $mount實(shí)戰(zhàn)之實(shí)現(xiàn)消息彈窗組件
之前的項(xiàng)目一直在使用Element-UI
框架,element中的Notification
、Message
組件使用時(shí)不需要在html寫(xiě)標(biāo)簽,而是使用js調(diào)用。那時(shí)就很疑惑,為什么element ui
使用this.$notify
、this.$message
就可以實(shí)現(xiàn)這樣的功能?
1、實(shí)現(xiàn)消息彈窗組件的幾個(gè)問(wèn)題
- 如何在任何組件中使用this.$message就可以顯示消息?
- 如何將消息的dom節(jié)點(diǎn)插入到body中?
- 同時(shí)出現(xiàn)多個(gè)消息彈窗時(shí),消息彈窗的z-index如何控制?
2、效果預(yù)覽
3、代碼實(shí)現(xiàn)
PMessage.vue
<template> <transition name="message-fade"> <div class="p-message" :class="[type, extraClass]" v-show="show" @mouseenter="clearTimer" @mouseleave="startTimer"> <div class="p-message-container"> <i class="p-message-icon" :class="`p-message-icon-${type}`"></i> <div class="p-message-content"> <slot class="p-message-content"> <div v-html="message"></div> </slot> </div> </div> </div> </transition> </template> <script> // 綁定事件 function _addEvent(el, eventName, fn){ if(document.addEventListener){ el.addEventListener(eventName, fn, false); }else if(window.attachEvent){ el.attactEvent('on' + eventName, fn); } }; // 解綁事件 function _offEvent(el, eventName, fn){ if(document.removeEventListener){ el.removeEventListener(eventName, fn, false); }else if(window.detachEvent){ el.detachEvent('on' + eventName, fn); } }; export default { name: "PMessage", data(){ return { type: 'success', duration: 3000, extraClass: '', message: '', timer: null, closed: false, show: false } }, methods: { startTimer(){ if(this.duration > 0){ this.timer = setTimeout(() => { if(!this.closed){ this.close(); } }, this.duration); } }, clearTimer(){ clearTimeout(this.timer); }, close(){ this.closed = true; if(typeof this.onClose === 'function'){ // 調(diào)用onClose方法,以從p-message.js中的instances數(shù)組中移除當(dāng)前組件,不移除的話(huà)就占空間了 this.onClose(); } }, // 銷(xiāo)毀組件 destroyElement(){ _offEvent(this.$el, 'transitionend', this.destroyElement); // 手動(dòng)銷(xiāo)毀組件 this.$destroy(true); this.$el.parentNode.removeChild(this.$el); }, }, watch: { // 監(jiān)聽(tīng)closed,如果它為true,則銷(xiāo)毀message組件 closed(newVal){ if(newVal){ this.show = false; // message過(guò)渡完成后再去銷(xiāo)毀message組件及移除元素 _addEvent(this.$el, 'transitionend', this.destroyElement); } } }, mounted() { this.startTimer(); } } </script> <style lang="stylus"> @import "p-message.styl" </style>
p-message.js
import Vue from 'vue'; import PMessage from './PMessage.vue'; import {popupManager} from "../../common/js/popup-manager"; let PMessageControl = Vue.extend(PMessage); let count = 0; // 存儲(chǔ)message組件實(shí)例,如需有關(guān)閉所有message的功能就需要將每個(gè)message組件都存儲(chǔ)起來(lái) let instances = []; const isVNode = function (node) { return node !== null && typeof node === 'object' && Object.prototype.hasOwnProperty.call(node, 'componentOptions'); }; const Message = function (options) { options = options || {}; if(typeof options === 'string'){ options = { message: options }; } let id = 'message_' + ++count; let userOnClose = options.onClose; // PMsesage.vue銷(xiāo)毀時(shí)會(huì)調(diào)用傳遞進(jìn)去的onClose,而onClose的處理就是將指定id的message組件從instances中移除 options.onClose = function (){ Message._close(id, userOnClose); }; /* 這里傳遞給PMessageControl的data不會(huì)覆蓋PMessage.vue中原有的data,而是與PMessage.vue中原有的data進(jìn)行合并,類(lèi)似 * 與mixin,包括傳遞methods、生命周期函數(shù)也是一樣 */ let instance = new PMessageControl({ data: options }); // 傳遞vNode if(isVNode(instance.message)){ instance.$slots.default = [instance.message]; instance.message = null; } instance.id = id; // 渲染元素,隨后使用原生appendChild將dom插入到頁(yè)面中 instance.$mount(); let $el = instance.$el; // message彈窗的z-index由popupManager來(lái)提供 $el.style.zIndex = popupManager.getNextZIndex(); document.body.appendChild($el); // 將message顯示出來(lái) instance.show = true; console.log(instance) instances.push(instance); return instance; }; // message簡(jiǎn)化操作 ['success','error'].forEach(function (item) { Message[item] = options => { if(typeof options === 'string'){ options = { message: options } } options.type = item; return Message(options); } }); /** * 從instances刪除指定message,內(nèi)部使用 * @param id * @param userOnClose * @private */ Message._close = function (id, userOnClose) { for(var i = 0, len = instances.length; i < len; i++){ if(instances[i].id === id){ if(typeof userOnClose === 'function'){ userOnClose(instances[i]); } instances.splice(i, 1); break; } } }; // 關(guān)閉所有message Message.closeAll = function () { for(var i = instances.length - 1; i >= 0; i--){ instances.close(); } }; export default Message;
popup-manager.js
let zIndex = 1000; let hasZIndexInited = false; const popupManager = { // 獲取索引 getNextZIndex(){ if(!hasZIndexInited){ hasZIndexInited = true; return zIndex; } return zIndex++; } }; export {popupManager}; p-index.js import pMessage from './p-message.js'; export default pMessage; p-message.styl .p-message{ position: fixed; top: 20px; left: 50%; padding: 8px 15px; border-radius: 4px; background-color: #fff; color: #000; transform: translateX(-50%); transition: opacity .3s, transform .4s; &.message-fade-enter, &.message-fade-leave-to{ opacity: 0; transform: translateX(-50%) translateY(-30px); } &.message-fade-enter-to, &.message-fade-leave{ opacity: 1; transform: translateX(-50%) translateY(0); } &.error{ color: #ff3737; } .p-message-icon{ /* 使圖標(biāo)與內(nèi)容能夠垂直居中 */ display: table-cell; vertical-align: middle; width: 64px; height: 45px; &.p-message-icon-success{ background: url("../../assets/images/icons/message-icon/icon_success.png") no-repeat 0 0; } &.p-message-icon-error{ background: url("../../assets/images/icons/message-icon/icon_error.png") no-repeat 0 0; } } .p-message-content{ /* 使圖標(biāo)與內(nèi)容能夠垂直居中 */ display: table-cell; vertical-align: middle; padding-left: 15px; } }
main.js
// 引入pMessage組件 import pMessage from './components/p-message/p-index.js'; // 將pMessage綁定到Vue.prototype中。這樣在組件中就可以通過(guò)this.$pMessage()的形式來(lái)使用了 Vue.prototype.$pMessage = pMessage;
相關(guān)文章
Vue全局注冊(cè)與局部注冊(cè)兩種組件注冊(cè)的方式
本文主要介紹了Vue全局注冊(cè)與局部注冊(cè)兩種組件注冊(cè)的方式,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-07-07vue中使用elementUI組件手動(dòng)上傳圖片功能
Vue是一套構(gòu)建用戶(hù)界面的框架, 開(kāi)發(fā)只需要關(guān)注視圖層, 它不僅易于上手,還便于與第三方庫(kù)或既有項(xiàng)目的整合。這篇文章主要介紹了vue中使用elementUI組件手動(dòng)上傳圖片,需要的朋友可以參考下2019-12-12Vue動(dòng)態(tài)權(quán)限登錄實(shí)現(xiàn)(基于路由與角色)
很多應(yīng)用都會(huì)需要對(duì)不同的用戶(hù)進(jìn)行權(quán)限控制,本文主要介紹了Vue動(dòng)態(tài)權(quán)限登錄實(shí)現(xiàn)(基于路由與角色),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-05-05vue3.0+vant3.0快速搭建項(xiàng)目的實(shí)現(xiàn)
本文主要介紹了vue3.0+vant3.0快速搭建項(xiàng)目的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-08-08vue v-for出來(lái)的列表,點(diǎn)擊某個(gè)li使得當(dāng)前被點(diǎn)擊的li字體變紅操作
這篇文章主要介紹了vue v-for出來(lái)的列表,點(diǎn)擊某個(gè)li使得當(dāng)前被點(diǎn)擊的li字體變紅操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-07-07vue3 表單搜索內(nèi)容回顯到地址欄的實(shí)例代碼
這篇文章主要介紹了vue3 表單搜索內(nèi)容回顯到地址欄的實(shí)例代碼,地址欄輸入內(nèi)容回顯到form表單,同理表單輸入內(nèi)容也要回顯到地址欄中,本文結(jié)合實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-09-09Vue利用localStorage本地緩存使頁(yè)面刷新驗(yàn)證碼不清零功能的實(shí)現(xiàn)
這篇文章主要介紹了Vue利用localStorage本地緩存使頁(yè)面刷新驗(yàn)證碼不清零功能的實(shí)現(xiàn),本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-09-09