首页 > 编程 > JavaScript > 正文

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

2019-11-19 11:44:10
字体:
来源:转载
供稿:网友

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

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

  • 如何在任何组件中使用this.$message就可以显示消息?
  • 如何将消息的dom节点插入到body中?
  • 同时出现多个消息弹窗时,消息弹窗的z-index如何控制?

2、效果预览

3、代码实现

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'){   // 调用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; } }};// 关闭所有messageMessage.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.jsimport 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{ /* 使图标与内容能够垂直居中 */ 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{ /* 使图标与内容能够垂直居中 */ display: table-cell; vertical-align: middle; padding-left: 15px; }}

main.js

// 引入pMessage组件import pMessage from './components/p-message/p-index.js';// 将pMessage绑定到Vue.prototype中。这样在组件中就可以通过this.$pMessage()的形式来使用了Vue.prototype.$pMessage = pMessage;

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表