手写简易版 Element-ui 弹框组件

369 阅读1分钟

1 组件核心流程图

image.png

2 具体分析流程步骤(Message)

  1. 创建Message.vue 组件
    1. 核心配置参数
    2. visible 代表弹框是否显示与隐藏
    3. message 消息提示内容
    4. duration 弹框显示的时间
    5. closed 关闭 与watch 进行结合,用于监听 visible 的改变
  2. 定义构造函数
let MessageConstructor = Vue.extend(Main);    // 形成指定组件的构造函数

  instance = new MessageConstructor({
    data: options   //接收消息弹框参数
  });

    instance.$mount();   // 通过将实例进行挂载 可以得到 $el
   document.body.appendChild(instance.$el);  // 将组件中的元素添加到body元素中
    instance.visible = true;   // 同时组件实例中的 visible 改变 进行显示
    return instance; // 返回组件
  1. 实例的挂在会触发 组件中 mounted 函数的执行
    1. 开始执行指定时间,会自动关闭弹框
    2. 关闭弹框时,由于watch的监听,会改变visible 状态
    3. 通过transition 嵌套的动画,也会 触发离开的事件的调用handleAfterLeave
    4. 将当前组件从Vue 实例中 销毁,同时从父节点中移除组件中的元素
<template>
  <transition name="el-message-fade" @after-leave="handleAfterLeave">
    <div
      :class="[
        'el-message',
        type && !iconClass ? `el-message--${ type }` : '',
        center ? 'is-center' : '',
        showClose ? 'is-closable' : '',
        customClass
      ]"
      :style="positionStyle"
      v-show="visible"
      @mouseenter="clearTimer"
      @mouseleave="startTimer"
      role="alert">
      <i :class="iconClass" v-if="iconClass"></i>
      <i :class="typeClass" v-else></i>
      <slot>
        <p v-if="!dangerouslyUseHTMLString" class="el-message__content">{{ message }}</p>
        <p v-else v-html="message" class="el-message__content"></p>
      </slot>
      <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,
        type: 'info',
        iconClass: '',
        customClass: '',
        onClose: null,
        showClose: false,
        closed: false,
        verticalOffset: 20,
        timer: null,
        dangerouslyUseHTMLString: false,
        center: false
      };
    },

    computed: {
      typeClass() {
        return this.type && !this.iconClass
          ? `el-message__icon el-icon-${ typeMap[this.type] }`
          : '';
      },
      positionStyle() {
        return {
          'top': `${ this.verticalOffset }px`
        };
      }
    },

    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);
        }
      },

      clearTimer() {
        clearTimeout(this.timer);
      },

      startTimer() {
        if (this.duration > 0) {
          this.timer = setTimeout(() => {
            if (!this.closed) {
              this.close();    // 执行一段时间后,会进行关闭
            }
          }, this.duration);
        }
      },
      keydown(e) {
        if (e.keyCode === 27) { // esc关闭消息
          if (!this.closed) {
            this.close();
          }
        }
      }
    },
    mounted() {
      this.startTimer();   // 开启定时器,由instance.$mount() 触发
      document.addEventListener('keydown', this.keydown);
    },
    beforeDestroy() {
      document.removeEventListener('keydown', this.keydown);
    }
  };
</script>

3 手写简易版

3.1 概念解释

extend:Vue 实例使用的根 DOM 元素。

propsData: 配置组件中的 props 接收的值, 相当于父传子的一种方式

  • 限制:只用于 new 创建的实例中。
  • 详细:创建实例时传递 props。主要作用是方便测试
var Comp = Vue.extend({
  props: ['msg'],
  template: '<div>{{ msg }}</div>'
    })

    var vm = new Comp({
      propsData: {
        msg: 'hello'
      }

vm.$mount( [elementOrSelector] )

var Comp = Vue.extend({
  props: ['msg'],
  template: '<div>{{ msg }}</div>'
    })

    var vm = new Comp({
      propsData: {
        msg: 'hello'
      }
    })

3.2 创建create 工具函数

export default function create (Comp, props) {
  const Ctor = Vue.extend(Comp)       //创建 组件的构造函数
  const intance = new Ctor({         // 创建组件的实例
    propsData: props                 // 配置组件的参数
  })

  intance.$mount() // 获取 $el       //  组件实例挂载
  document.body.appendChild(intance.$el)    // 组件的根元素以及内容添加到body

  intance.remove = () => {      // 往组件实例中添加 销毁 和 移除元素
    intance.$destroy()

    document.body.removeChild(intance.$el)
  }

  return intance     // 返回组件实例
}

3.3 Vue实例中添加方法

import create from './utils/create'
import HuiNotice from './components/HuiNotice.vue'

Vue.prototype.$notice = (props) => {
  const comp = create(HuiNotice, props)
  comp.show()
  return comp
}

3.4 登录校验未通过进行使用

onLogin () {
  // console.log(11)
  const formInstance = this.$refs.form
  formInstance.validate((vaild) => {
    if (vaild) {
      console.log('校验通过')
    } else {
      console.log('校验没有通过')

      // create(HuiNotice, {
      //   title: '温馨提示:',
      //   message: '校验没有通过',
      //   duration: 3000
      // }).show()
      this.$notice({
        title: '温馨提示:',
        message: '校验没有通过',
        duration: 3000
      })
    }
  })
}
  },