如何写出一个confirm组件

2,270 阅读2分钟

前言

一直都想写一个组件库,作为一个前端的我一直都有这么一个梦想,在写项目的过程中一直都在引用别人的组件库长久而言自己也开始想写一个组件库。什么是组件呢?这里不再过的赘述,下面我们先谈一谈confirm组件的封装过程。在有些平台使用过程中删除操作常常伴随着确认的情况毕竟要避免客户的误操作么,废话不多说开怼!!!

我是确认框

第一个了解一些前置的API

Vue.extend方法

extend
这是vue的官方文档可能看到了有点蒙蔽,extend创建的是一个组件构造器,而不是一个具体的组件实例。通常是配合组件使用的,因为我们最终需要使用this.$confirm的方式进行调用。那么我们需要这个方法来构造一个组件。 如何全局引入组件

const components = {
    ConfirmModal
}
const install = function (Vue) {
  if (install.installed) return
  Object.keys(components).forEach(key => {
    Vue.component(components[key].name, components[key])
  })

  Vue.prototype.$confirm=ConfirmModal
}

if (typeof window !== 'undefined' && window.Vue) {
  install(window.Vue)
}

const API = {
  install,
  ...components
}

上面的代码里面components代表数组对象我们同过Object.keys的方式使用Vue.component语法将组件注册。上面有一行将confirm的对象挂载到Vue对象的原型链下,我们就可以通过this.confirm的方法调用这个组件了。

开始写组件

组件代码如下:

<template>
    <div class="alertModal">
        <!--social post弹框-->
        <transition name="fade">
            <div class="alertbox">
                <div class="alert-dialog">
                    <div class="alert-content">
                        <div class="alert-header">
                            <span class="alertclose" @click="close">×</span>
                            <span class="alert-title">{{modelTitle}}</span>
                        </div>
                        <div class="alert-body">{{modelContent}}</div>
                        <div class="alert-footer">
                            <button class="alertbtn" @click="close">{{modelClose}}</button>
                            <button class="alertbtn alert-info" @click="confirm">{{modelSave}}</button>
                        </div>
                    </div>
                </div>
            </div>
        </transition>
        <div class="modal-backdrop"></div>
    </div>
</template>

<script>
import confirmMixin from'./confirmMixin.js'
export default {
  mixins:[confirmMixin],
  props: {
    modelTitle: {
      type: String,
      default: "确认框" 
    },
    modelSave: {
      type: String,
      default: "确认"
    },
    modelClose: {
      type: String,
      default: "取消"
    },
    modelContent: {
      type: String,
      default: "我是确认框"
    },
    callBack: {
      type: Function,
      default() {}
    },
    closeAction: {
      type: Function,
      default() {}
    }
  },
  methods: {
    close() {
      this.$emit("close");
      this.isVisible = false;
      this.closeAction();
      this.destroyed();
    },
    //点击确定按钮弹窗消失
    confirm() {
      this.$emit("confirm");
      this.isVisible = false;
      this.callBack();
      this.destroyed();
    },
    destroyed() {
      setTimeout(() => {
        this.$destroy();
      }, 200);
    }
  },
  mounted() {
    setTimeout(() => {
      this.isVisible = true;
    }, 100);
  }
};
</script>
<style  scoped>
.modal.fade .alert-dialog {
  -webkit-transition: -webkit-transform 0.3s ease-out;
  -o-transition: -o-transform 0.3s ease-out;
  transition: transform 0.3s ease-out;
  -webkit-transform: translate(0, -25%);
  -ms-transform: translate(0, -25%);
  -o-transform: translate(0, -25%);
  transform: translate(0, -25%);
}
.modal.in .alert-dialog {
  -webkit-transform: translate(0, 0);
  -ms-transform: translate(0, 0);
  -o-transform: translate(0, 0);
  transform: translate(0, 0);
}
.alertbox {
  position: fixed;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  text-align: center;
  z-index: 99999;
}
.alert-dialog {
  display: inline-block;
  width: 420px;
  padding-bottom: 10px;
  vertical-align: middle;
  background-color: #fff;
  border-radius: 4px;
  border: 1px solid #e6ebf5;
  font-size: 18px;
  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
  text-align: left;
  overflow: hidden;
  backface-visibility: hidden;
  position: relative;
  top: 140px;
  padding: 10px 15px;
}
.modal-backdrop.fade {
  filter: alpha(opacity=0);
  opacity: 0;
}
.modal-backdrop.in {
  filter: alpha(opacity=50);
  opacity: 0.5;
}
.alert-footer {
  float: right;
  margin-top: 5px;
}
.alert-scrollbar-measure {
  position: absolute;
  top: -9999px;
  width: 50px;
  height: 50px;
  overflow: scroll;
}

.fade-enter-active,
.fade-leave-active {
  transition: opacity 0.5s;
}
.fade-enter, .fade-leave-to /* .fade-leave-active in <2.1.8 */ {
  opacity: 0;
}
.modal-backdrop {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  z-index: 1040;
  background-color: #000;
  opacity: 0.5;
}

.el-icon-date {
  cursor: pointer;
}
.alert-header {
}
.alert-title {
  font-size: 18px;
  line-height: 1;
  color: #2d2f33;
}
.alert-body {
  padding: 10px 0px;
  color: #5a5e66;
  font-size: 14px;
  line-height: 17px;
}
.alertbtn {
  text-align: center;
  font-weight: 500;
  cursor: pointer;
  padding: 9px 15px;
  font-size: 12px;
  border-radius: 3px;
  line-height: 1;
  background: #fff;
  border: 1px solid #d8dce5;
  border-color: #d8dce5;
  color: #5a5e66;
}
.alert-info {
  color: #fff;
  background-color: #409eff;
  border-color: #409eff;
}
.alertclose {
  float: right;
  cursor: pointer;
}
</style>

可以看到里面使用了mixin的语法我把有些公共的逻辑抽离了出来万一能复用呢?

const ConfirmMixin = {
    props: {
      isVisible: {
        type: Boolean,
        default: false
      }
    },
    data () {
      return {
        isActive: false
      }
    },
    methods: {
      active () {
        this.isActive = true
      },
      close () {
        this.$emit('close')
        this.isActive = false
      }
    },
  
    watch: {
      isVisible (val) {
        this.isActive = val
      }
    },
  
    mounted () {
      this.$nextTick(() => {
        document.body.appendChild(this.$el)
        if (this.isVisible) {
          this.active()
        }
      })
    },
  
    beforeDestroy () {
      this.$el.remove()
    }
  }
  export default ConfirmMixin;

这个就是mixin的代码

最后写一个入口文件 index.JS文件这个文件非常关键将会使用上面我们说到的API创建组建的构造器 然后实例化组件并返回一个方法。

import Vue from 'vue'
import confirm from './confirm.vue'

function open(propsData) {
  const confirmComponent = Vue.extend(confirm);
  return new confirmComponent({
    el: document.createElement('div'),
    propsData
  })
}
export default {
  confirm(opts) {
    const defaultOpts={ modelTitle: "确认框",modelSave:  "确认",modelClose: "取消",modelContent:  "我是确认框",callBack() {},closeAction() {}}
    const propsOpts=Object.assign(defaultOpts,opts);
    return open(propsOpts)
  }
}

这就是index.js的主要逻辑,我们构造过一个组件之后,就可以使用vnode里面的一些属性,我们可以通过这个属性吧组件需要的值传递过去。列入这里面的props和el元素。最后写一个confirm的方法并返回这个方法,方法里面主要是一些默认配置,当没有内容合并进去的时候就会展示这个默认值。

如何使用

我们如何这个组件呢?

this.$confirm.confirm({
        modelTitle: "确认框",modelSave:  "确认",modelClose: "取消",modelContent:  "我是确认框",callBack() {
          alert(1111)
        },closeAction() {alert(222)}
      })

最后的效果

效果图

写在最后

哈哈最后写一个组件也是挺费劲的得想到各种情况,各种回调函数如何调用。一个小小的组件多么的考验js的功底,撒花完成。