资讯专栏INFORMATION COLUMN

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

lvzishen / 2025人阅读

摘要:之前的项目一直在使用框架,中的组件使用时不需要在写标签,而是使用调用。这样在组件中就可以通过的形式来使用了参考参考了的代码封装组件的一些技巧

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

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

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

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

2、效果预览

3、代码实现

PMessage.vue





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

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{ /* 使图标与内容能够垂直居中 */
    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;
3、参考

参考了 Element-UImessage代码

封装Vue组件的一些技巧

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/103698.html

相关文章

  • 封装Vue组件的一些技巧

    摘要:根据组件单向数据流和和事件通信机制,需要由子组件通过事件通知父组件,并在父组件中修改原始的数据,完成状态的更新。 本文同步在个人博客shymean.com上,欢迎关注 写Vue有很长一段时间了,除了常规的业务开发之外,也应该思考和反思一下封装组件的正确方式。以弹窗组件为例,一种实现是在需要模板中引入需要弹窗展示的组件,然后通过一个flag变量来控制弹窗的组件,在业务代码里面会充斥着冗余的弹...

    韩冰 评论0 收藏0
  • vue弹窗插件实战

    摘要:组件显示隐藏由内部属性控制,只暴露给外界和个方法,个方法触发对应的事件测试一下弹窗内容插件化组件功能写好了,但是这种调用方式显得很累赘。我们写的弹窗能不能这么方便呢,为此需要把改写成插件。 vue做移动端经常碰到弹窗的需求, 这里写一个功能简单的vue弹窗 popup.vue {{text}} 组件html结构, 外层divposition:fixed定位, 内...

    ZweiZhao 评论0 收藏0
  • 从Dialog管理谈到Vue渲染原理

    摘要:在组件内,我们触及不到组件的模板,所以简单的在动态模板上添加并不能完成事件监听。简单来说,依赖收集是在渲染函数渲染的函数中进行的,在中一旦通过使用了这个变量,通过这个变量的就收集到了正在执行的渲染函数这一个依赖。 作为一个中后台表单&表格工程师,经常需要在一个页面中处理多个弹窗。我自己的项目中,一个复杂的审核页面中的弹窗数量超过了30个,如何管理大量的弹窗就成为了一个需要考虑的问题。 ...

    darkbug 评论0 收藏0
  • Vue.extend用法(主要用于需要 动态渲染的组件,或者类似于window.alert() 提示

    摘要:一简单的使用主要用于需要动态渲染的组件,或者类似于提示组件注意创建的是一个组件构造器,而不是一个具体的组件实例属于的全局,在实际业务开发中我们很少使用,因为相比常用的写法使用步骤要更加繁琐一些。 最近在重构公司的项目,有些组件想要封装的更灵活,例如toast、loading、messageBox等弹窗组件,模仿了mint-ui封装此类组件的方式封装了自己的弹窗组件; mint-UI的t...

    masturbator 评论0 收藏0
  • 手把手教你撸个vue2.0弹窗组件

    摘要:组件结构同组件结构通过方法获取元素的大小及其相对于视口的位置,之后对提示信息进行定位。可以用来进行一些复杂带校验的弹窗信息展示,也可以只用于简单信息的展示。可以通过属性来显示任意标题,通过属性来修改显示区域的宽度。 手把手教你撸个vue2.0弹窗组件 在开始之前需要了解一下开发vue插件的前置知识,推荐先看一下vue官网的插件介绍 预览地址 http://haogewudi.me/k...

    mrli2016 评论0 收藏0

发表评论

0条评论

lvzishen

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<