Element中你不知道的Message! ! !

232 阅读2分钟

ElementMessage组件相信大家用的都不少,但你真的熟悉Message组件吗?

你不知道的Message

一般情况下,我们会这样去使用Message组件:

import { Message } from 'element-ui';
Message({
    message: "调用成功",
    type: "success",
})
​
Message.success("调用成功")

但是当组件渲染时,我们尝试点击键盘上的ESC键,会发现所有的Message弹窗都关闭了。这是怎么一回事呢??

我们再次尝试,在一个点击事件中,多次调用弹窗提示

<template>
    <div @click="open">多次提示</div>
</template>
<script>
import { Message } from 'element-ui';
...
methods: {
    open(){
        Message.success("1")
        Message.success("2")
        Message.success("3")
        Message.success("4")
        Message.success("5")
        Message.success("6")
    },
},
...
</script>

当触发open事件时,我们再次点击键盘上的ESC键,所有的弹窗组件还是全部关闭了。

这时我们就要打开Message源码一探究竟了。

image-20231012143025770.png

image-20231012143329584.png

image-20231012143506872.png

在源码element\packages\message\src\main.vue中存在上述一段代码

哦!!!这下我们明白了,原来在每次组件渲染完成时,都开启了一个keydown事件监听,如果我们按下的键keyCode === 27(按下键是Esc)时,就会去判断当前组件的closed状态,如果是没有关闭就会调用this.close() 关闭当前弹窗。

看到这,我们会想Message又是如何进行组件挂载和销毁的呢??

Message组件是如何进行挂载与销毁的

Message组件实现主要依赖于这两个文件:

image-20231012145318077.png

挂载组件:

//main.js
...
import Main from './main.vue';
...
let MessageConstructor = Vue.extend(Main);
...
const Message = function(options) {
  if (Vue.prototype.$isServer) return;
  options = options || {};
  if (typeof options === 'string') {
    options = {
      message: options
    };
  }
  let userOnClose = options.onClose;
  let id = 'message_' + seed++;
​
  options.onClose = function() {
    Message.close(id, userOnClose);
  };
​
  instance = new MessageConstructor({
    data: options
  });
  instance.id = id;
  if (isVNode(instance.message)) {
    instance.$slots.default = [instance.message];
    instance.message = null;
  }
  instance.$mount();
  document.body.appendChild(instance.$el);
  let verticalOffset = options.offset || 20;
  instances.forEach(item => {
    verticalOffset += item.$el.offsetHeight + 16;
  });
  instance.verticalOffset = verticalOffset;
  instance.visible = true;
  instance.$el.style.zIndex = PopupManager.nextZIndex();
  instances.push(instance);
  return instance;
};

当我们调用Message({ message: "调用成功", type: "success"}) 时,执行的就是上述Message方法。

首先,通过Vue.extend(Main) 创建一个”子类“,然后new MessageConstructor({data: options}) 实例化组件对象,最后通过instance.mount()以及document.body.appendChild(instance.mount()** 以及**document.body.appendChild(instance.el) 完成组件的挂载(渲染)。

销毁组件:

//main.vue
<template>
  <transition name="el-message-fade" @after-leave="handleAfterLeave">
    <div
      ...
      v-show="visible"
      ...
    >
      ...
    </div>
  </transition>
</template>
<script type="text/babel">
     export default {
        data() {
            ...
            visible: false,
            ...
        }
        ...
        watch: {
            closed(newVal) {
                if (newVal) {
                    this.visible = false;
                }
            }
        },
        ...
        methods: {
            handleAfterLeave() {
                this.$destroy(true);
                this.$el.parentNode.removeChild(this.$el);
            },
            close() {
                this.closed = true;
                if (typeof this.onClose === 'function') {
                    this.onClose(this);
                }
            },
            ...
        }
      }
</script>

通过调用close方法,watch监听closed会将this.visible赋值为false,从而过渡钩子handleAfterLeave会被执行,this.destroy(true)会销毁当前组件,this.destroy(true)** 会销毁当前组件,**this.el.parentNode.removeChild(this.$el) 会将节点从父节点中移除。

现在我们也能通过Vue.extend以及 $mount自己封装一个message组件了!!!

觉得对你有帮助的麻烦请点个小小的赞,谢谢。^o^