Vue $mount实战之实现消息弹窗组件

之前的项目一直在使用Element-UI框架,element中的Notification、Message组件使用时不需要在html写标签,而是使用js调用。那时就很疑惑,为什么element ui使用this.$notify、this.$message就可以实现这样的功能?

1、实现消息弹窗组件的几个问题

如何在任何组件中使用this.$message就可以显示消息?

如何将消息的dom节点插入到body中?

同时出现多个消息弹窗时,消息弹窗的z-index如何控制?

2、效果预览

Vue $mount实战之实现消息弹窗组件

3、代码实现

PMessage.vue

<template> <transition> <div :class="[type, extraClass]" v-show="show" @mouseenter="clearTimer" @mouseleave="startTimer"> <div> <i :class="`p-message-icon-${type}`"></i> <div> <slot> <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'){ // 调用onClose方法,以从p-message.js中的instances数组中移除当前组件,不移除的话就占空间了 this.onClose(); } }, // 销毁组件 destroyElement(){ _offEvent(this.$el, 'transitionend', this.destroyElement); // 手动销毁组件 this.$destroy(true); this.$el.parentNode.removeChild(this.$el); }, }, watch: { // 监听closed,如果它为true,则销毁message组件 closed(newVal){ if(newVal){ this.show = false; // message过渡完成后再去销毁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; // 存储message组件实例,如需有关闭所有message的功能就需要将每个message组件都存储起来 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销毁时会调用传递进去的onClose,而onClose的处理就是将指定id的message组件从instances中移除 options.onClose = function (){ Message._close(id, userOnClose); }; /* 这里传递给PMessageControl的data不会覆盖PMessage.vue中原有的data,而是与PMessage.vue中原有的data进行合并,类似 * 与mixin,包括传递methods、生命周期函数也是一样 */ let instance = new PMessageControl({ data: options }); // 传递vNode if(isVNode(instance.message)){ instance.$slots.default = [instance.message]; instance.message = null; } instance.id = id; // 渲染元素,随后使用原生appendChild将dom插入到页面中 instance.$mount(); let $el = instance.$el; // message弹窗的z-index由popupManager来提供 $el.style.zIndex = popupManager.getNextZIndex(); document.body.appendChild($el); // 将message显示出来 instance.show = true; console.log(instance) instances.push(instance); return instance; }; // message简化操作 ['success','error'].forEach(function (item) { Message[item] = options => { if(typeof options === 'string'){ options = { message: options } } options.type = item; return Message(options); } }); /** * 从instances删除指定message,内部使用 * @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; } } }; // 关闭所有message Message.closeAll = function () { for(var i = instances.length - 1; i >= 0; i--){ instances.close(); } }; export default Message;

popup-manager.js

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/d3633a684109527d8765220b614de806.html