深入理解 el-dialog 组件源码

1,073 阅读1分钟

前言

el-dialog 是 Element UI 框架中的一个常用组件,用于实现弹窗对话框的展示和交互。它提供了丰富的功能和配置选项,可以满足不同场景下的需求。本文将深入探讨 el-dialog的源码

基本使用

组件使用

<el-button @click="showDialog">打开对话框</el-button>
<my-dialog :visible.sync="visible" title="弹框" center>
  <el-input ></el-input>
  <div>弹框内容</div>
  <div slot="footer">
    <el-button>确定</el-button>
    <el-button @click="visible = false">取消</el-button>
  </div>
</my-dialog>

源码

思路

  • 弹框组件一开始是隐藏,打开弹框的同时会显示弹框内容,增加遮罩层,层级比弹框低
  • 遮罩层也可以插入到body里面,只需将modalAppendToBody:true
  • 弹框内容默认在当前页面,如果设置appendToBody:true,将会把弹框元素插入到body
  • 关闭弹框时,去掉遮罩层,同时通过v-show隐藏弹框元素

实现代码

<template>
  <div
    v-show="visible"
    class="el-dialog__wrapper"
    @click.self="handleWrapperClick"
  >
    <div :key="key" class="el-dialog" ref="dialog" :style="style">
      <div class="el-dialog__header">
        <slot name="title">
          <span class="el-dialog__title">{{ title }}</span>
        </slot>
      </div>
      <div class="el-dialog__body" v-if="rendered"><slot></slot></div>
      <div class="el-dialog__footer" v-if="$slots.footer">
        <slot name="footer"></slot>
      </div>
    </div>
  </div>
</template>
<script>
let idSeed = 1;
let modalDom = null;
export default {
  props: {
    title: {
      type: String,
      default: "",
    },
    modalAppendToBody: {
      type: Boolean,
      default: false,
    },

    appendToBody: {
      type: Boolean,
      default: false,
    },

    closeOnClickModal: {
      type: Boolean,
      default: true,
    },

    width: String,

    visible: Boolean,

    top: {
      type: String,
      default: "15vh",
    },
  },
  data() {
    return {
      key: 0,
      opened: false,
      rendered: false,
      zIndex: 2000,
    };
  },
  computed: {
    style() {
      let style = {};
      style.marginTop = this.top;
      if (this.width) {
        style.width = this.width;
      }
      return style;
    },
  },
  watch: {
    visible(val) {
      if (val) {
        this.rendered = true;
        this.open();
        if (this.appendToBody) {
          document.body.appendChild(this.$el);
        }
      } else {
        this.close();
      }
    },
  },
  methods: {
    // 点击遮罩层,关闭
    handleWrapperClick() {
      if (!this.closeOnClickModal) return;
      this.handleClose();
    },
    // 关闭弹框
    handleClose() {
      this.$emit("update:visible", false);
    },
    // 插入遮罩层
    open() {
      if (this.opened) return;
      this._opening = true;
      const dom = this.$el;

      if (this._closing) {
        this.closeModal(this._popupId);
        this._closing = false;
      }
      // 增加遮罩层
      this.openModal(
        this._popupId,
        ++this.zIndex,
        this.modalAppendToBody ? undefined : dom
      );

      // 弹框增加一个层级
      dom.style.zIndex = ++this.zIndex;
      this.opened = true;
      this._opening = false;
    },
    close() {
      this._closing = true;
      this.opened = false;
      this.closeModal(this._popupId);
      this._closing = false;
    },
    openModal(id, zIndex, dom) {
      // 创建div,注册滚动和点击事件
      const modalDom = this.getModal();
      modalDom.className = "v-modal";
      modalDom.style.zIndex = zIndex;
      modalDom.tabIndex = 0;
      modalDom.style.display = "";

      // 遮罩层加在页面
      if (dom && dom.parentNode && dom.parentNode.nodeType !== 11) {
        dom.parentNode.appendChild(modalDom);
      } else {
        document.body.appendChild(modalDom);
      }
    },
    closeModal() {
      if(!modalDom) {
        return
      }
      if (modalDom.parentNode) modalDom.parentNode.removeChild(modalDom);
      modalDom.style.display = "none";
      modalDom = undefined;
    },
    getModal() {
      if (!modalDom) {
        modalDom = document.createElement("div");
        modalDom.addEventListener("click", () => {
          if (this.closeOnClickModal) {
            this.close();
          }
        });
      }
      return modalDom;
    },
  },
  mounted() {
    // 一开始渲染弹框
    if (this.visible) {
      this.rendered = true;
      this.open();
      if (this.appendToBody) {
        document.body.appendChild(this.$el);
      }
    }
  },
  beforeMount() {
    this._popupId = "popup-" + idSeed++;
  },
  beforeDestroy() {
    this.closeModal(this._popupId); // 关闭弹框
    modalDom = null;
  },
};
</script>

最后

在本文中,我们深入了解了 el-dialog 组件的使用方法。通过灵活地使用 el-dialog 组件,我们可以实现各种各样的弹窗对话框,并满足不同场景下的需求。希望本文能够帮助你更好地理解和使用 el-dialog 组件,实现代码根据源代码改造!