zoukankan      html  css  js  c++  java
  • element-ui Message组件源码分析整理笔记(八)

    Message组件源码:

    main.js

    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;
    

    main.vue

    <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>
    
  • 相关阅读:
    19牛客暑期多校 round2 H 01矩阵内第二大矩形
    NOIP2017滚粗记
    Left 4 Dead 2(求生之路2) 游戏打不开 游戏闪退 的一种可能性以及解决方法
    Luogu P1156 垃圾陷阱
    Luogu P1376 机器工厂
    Luogu P1842 奶牛玩杂技
    Luogu P1880 石子合并
    Luogu P1441 砝码称重(fj省选)
    Luogu P1077 摆花
    Luogu P1282 多米诺骨牌
  • 原文地址:https://www.cnblogs.com/fangnianqin/p/10103216.html
Copyright © 2011-2022 走看看