最新文章专题视频专题问答1问答10问答100问答1000问答2000关键字专题1关键字专题50关键字专题500关键字专题1500TAG最新视频文章推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37视频文章20视频文章30视频文章40视频文章50视频文章60 视频文章70视频文章80视频文章90视频文章100视频文章120视频文章140 视频2关键字专题关键字专题tag2tag3文章专题文章专题2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章专题3
当前位置: 首页 - 科技 - 知识百科 - 正文

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

来源:动视网 责编:小采 时间:2020-11-27 21:58:24
文档

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

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


之前的项目一直在使用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;
     }
     }
    };
    // 关闭所有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;

    文档

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

    Vue $mount实战之实现消息弹窗组件:之前的项目一直在使用Element-UI框架,element中的Notification、Message组件使用时不需要在html写标签,而是使用js调用。那时就很疑惑,为什么element ui使用this.$notify、this.$message就可以实现这样的功能? 1、实现消息弹窗组件的几个问题
    推荐度:
    标签: VUE 实现 弹窗
    • 热门焦点

    最新推荐

    猜你喜欢

    热门推荐

    专题
    Top