Message組件源碼:javascript
import Vue from 'vue'; import Main from './main.vue'; import { PopupManager } from 'element-ui/src/utils/popup'; import { isVNode } from 'element-ui/src/utils/vdom'; let MessageConstructor = Vue.extend(Main); let instance; let instances = []; // 存放當前未close的message let seed = 1; const Message = function(options) { // 當前 Vue 實例是否運行於服務器 if (Vue.prototype.$isServer) return; options = options || {}; if (typeof options === 'string') { options = { message: options }; } //userOnClose用來存放用戶設置關閉時的回調函數, 參數爲被關閉的 message 實例 let userOnClose = options.onClose; let id = 'message_' + seed++; // 重寫options.onClose options.onClose = function() { Message.close(id, userOnClose); }; // 建立message實例,此時數據尚未掛載呢,$el 屬性目前不可見,沒法訪問到數據和真實的dom instance = new MessageConstructor({ data: options }); instance.id = id; //判斷instance.message是否是虛擬節點 if (isVNode(instance.message)) { instance.$slots.default = [instance.message]; instance.message = null; } //手動地掛載一個未掛載的實例。$mount(param)中param不存在時,模板將被渲染爲文檔以外的的元素,而且你必須使用原生 DOM API 把它插入文檔中。 instance.vm = instance.$mount(); //用原生DOM API把它插入body中 document.body.appendChild(instance.vm.$el); instance.vm.visible = true; instance.dom = instance.vm.$el; // css z-index層級疊加,覆蓋以前已出現但還未close的message instance.dom.style.zIndex = PopupManager.nextZIndex(); instances.push(instance); return instance.vm; }; // 給Message增長四個直接調用的方法 // 支持this.$message.success('xxx')方式調用,等同於this.$message({type: 'success',message: 'xxx'}) ['success', 'warning', 'info', 'error'].forEach(type => { Message[type] = options => { if (typeof options === 'string') { options = { message: options }; } options.type = type; return Message(options); }; }); // 組件的close方法中調用onClose再調該方法 Message.close = function(id, userOnClose) { for (let i = 0, len = instances.length; i < len; i++) { if (id === instances[i].id) { // 經過id找到該message實例 if (typeof userOnClose === 'function') { userOnClose(instances[i]); } instances.splice(i, 1); // 移除message實例 break; } } }; //關閉全部的消息提示彈窗 Message.closeAll = function() { for (let i = instances.length - 1; i >= 0; i--) { instances[i].close(); } }; export default Message;
<template> <transition name="el-message-fade"> <div :class="[ 'el-message', type && !iconClass ? `el-message--${ type }` : '', center ? 'is-center' : '', showClose ? 'is-closable' : '', customClass ]" v-show="visible" @mouseenter="clearTimer" @mouseleave="startTimer" role="alert"> <!--自定義圖標存在時顯示--> <i :class="iconClass" v-if="iconClass"></i> <!--自定義圖標不存在時根據type顯示圖標--> <i :class="typeClass" v-else></i> <slot> <!--用戶設置的message的參數爲字符串時,顯示字符串--> <p v-if="!dangerouslyUseHTMLString" class="el-message__content">{{ message }}</p> <!--用戶設置的message的參數爲VNode時,在此處顯示--> <p v-else v-html="message" class="el-message__content"></p> </slot> <!--當用戶設置的關閉按鈕顯示爲true時,顯示關閉圖標--> <i v-if="showClose" class="el-message__closeBtn el-icon-close" @click="close"></i> </div> </transition> </template> <script type="text/babel"> const typeMap = { success: 'success', info: 'info', warning: 'warning', error: 'error' }; export default { data() { return { visible: false, message: '', //消息文字 duration: 3000, //顯示時間, 毫秒。設爲 0 則不會自動關閉 type: 'info', iconClass: '', //自定義圖標的類名,會覆蓋 type customClass: '', //自定義類名 onClose: null, showClose: false, //是否顯示關閉按鈕 closed: false, //用來判斷消息提示彈窗是否關閉 timer: null, dangerouslyUseHTMLString: false, //是否將 message 屬性做爲 HTML 片斷處理 center: false }; }, computed: { // 根據type返回對應的圖標類名 typeClass() { return this.type && !this.iconClass ? `el-message__icon el-icon-${ typeMap[this.type] }` : ''; } }, watch: { closed(newVal) { if (newVal) { this.visible = false; //transitionend事件在 CSS 完成過渡後觸發。 this.$el.addEventListener('transitionend', this.destroyElement); } } }, methods: { destroyElement() { this.$el.removeEventListener('transitionend', this.destroyElement); //徹底銷燬一個實例。清理它與其它實例的鏈接,解綁它的所有指令及事件監聽器。 // 在vue v1.x中$destroy(true)的參數爲true時,則從DOM中刪除其關聯的DOM元素或片斷;在vue2.0中不須要加參數 this.$destroy(true); this.$el.parentNode.removeChild(this.$el); }, close() { this.closed = true; if (typeof this.onClose === 'function') { this.onClose(this); } }, //鼠標進入消息提示彈窗時,定時器清空,彈窗一直顯示 clearTimer() { clearTimeout(this.timer); }, // 鼠標離開消息提示彈窗時,設置定時器,彈窗在this.duration關閉 startTimer() { if (this.duration > 0) { this.timer = setTimeout(() => { if (!this.closed) { this.close(); } }, this.duration); } }, // esc關閉消息 keydown(e) { if (e.keyCode === 27) { if (!this.closed) { this.close(); } } } }, mounted() { this.startTimer(); document.addEventListener('keydown', this.keydown); }, beforeDestroy() { document.removeEventListener('keydown', this.keydown); } }; </script>