文章目录

  • 一、前言
  • 二、弹窗组件代码封装
  • 三、简单分析
  • 四、Vue.use之前的准备
  • 五、main文件的配置及组件的使用
  • 六、总结


一、前言

   学习vue也有一段时间了,通过一段时间的学习,逐渐了解了vue框架的一些用法,目前我能感受到vue框架最强大的功能之一就是组件,即代码的复用。而在之前的写项目中经常会用到一些弹窗,在之前的项目中这些弹窗都是写在一个模板页面中,去调用。在学了vue之后我就想着能不能用vue去封装一个全局的弹窗组件,可以使用类似vuex的的方式通过this.$xxx的方式直接调用。

效果演示


封装组件演示


二、弹窗组件代码封装
<template>
  <div class="myAlertDiv" v-show="show">
    <div class="myAlertItem" v-show="type === 1">
      <button class="iconSty" @click="cancelBtnFn">
        <i class="el-icon-close"></i>
      </button>
      <div class="tittle">{{ title }}</div>
      <div class="msg" v-text="message"></div>
      <div class="operator">
        <button @click="sureBtnFn" class="generalSty suerSty hoverSty">
          {{ sureMsg }}
        </button>
        <button @click="cancelBtnFn" class="generalSty cancelSty hoverSty">
          {{ cancelMsg }}
        </button>
      </div>
    </div>
    <div class="hintMsg hintGeneralSty animationSty" v-show="type === 2">
      <span>
        <i class="el-icon-warning-outline"></i>
      </span>
      <span>
        {{ message }}
      </span>
    </div>
    <div class="warningMsg hintGeneralSty animationSty" v-show="type === 3">
      <span>
        <i class="el-icon-warning-outline"></i>
      </span>
      <span>
        {{ message }}
      </span>
    </div>
    <div class="successMsg hintGeneralSty animationSty" v-show="type === 4">
      <span>
        <i class="el-icon-circle-check"></i>
      </span>
      <span>
        {{ message }}
      </span>
    </div>
  </div>
</template>

<script>
import { Icon } from "element-ui";
export default {
  data() {
    return {
      // 提示标题
      title: "提示",
      //   提示内容
      message: "提示信息",
      //   确定按钮内容
      sureMsg: "确定",
      //   取消按钮内容
      cancelMsg: "取消",
      //   用于指定是什么类型的提示信息,1表示带确定和取消的弹窗,2表示提示信息,3表示警告,4表示成功
      type: 4,
      //   是否展示
      show: false,
    };
  },
  //这里用到了element-ui的图标,因为这里只在这个组件中使用了,所以就局部注册了图标组件
  components: {
    [Icon.name]: Icon,
  },
  methods: {
    // 初始化函数
    init() {
      this.show = true;
      //当类型是1时,弹窗带取消和确定按钮,可以通过this.$xx的方式调用之后通过.then(表示确定)或.catch(表示取消)来表示是取消还是成功
      if (this.type == 1) {
        return new Promise((resolve, resject) => {
          this.promiseResult = { resolve, resject };
        });
      } else {
      //类型不是1,则是提示弹窗,不需要确定按钮和取消按钮,但是需要等动画执行完毕之后再销毁组件实例
        // 销毁组件(需等动画执行完)
        setTimeout(() => {
          //执行销销毁组件实例函数
          this.destruction();
        }, 1500);
      }
    },
    // 确定函数
    sureBtnFn() {
      this.promiseResult.resolve();
      //执行销毁组件实例函数
      this.destruction();
    },
    // 取消函数
    cancelBtnFn() {
      this.promiseResult.resject();
      this.destruction();
    },
    // 销毁函数
    destruction() {
      this.show = false;
      //   销毁实例
      this.$destroy(true);
      //将组件从body中移除
      this.$el.parentNode.removeChild(this.$el);
    },
  },
};
</script>
<style scoped>
button {
  outline: none;
  background-color: white;
  color: white;
  border: none;
  cursor: pointer;
}

.myAlertDiv {
  width: 100%;
  height: 100%;
  position: fixed;
  background-color: rgba(0, 0, 0, 0.5);
  top: 0px;
  left: 0px;
}

.myAlertDiv > div {
  padding: 10px;
  position: relative;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -150%);
  text-align: center;
}

.myAlertItem {
  width: 500px;
  min-height: 100px;
  max-height: 400px;
  background-color: white;
}

.tittle {
  font-size: 20px;
  font-weight: bold;
}

.msg {
  margin-top: 20px;
  word-wrap: break-word;
  max-height: 100px;
  overflow: auto;
}

.generalSty {
  padding: 5px 15px;
  margin-top: 10px;
  margin-right: 20px;
}

.suerSty {
  background-color: #67c23a;
}

.cancelSty {
  background-color: #f56c6c;
}

.hoverSty:hover {
  opacity: 0.8;
}

.iconSty {
  position: absolute;
  right: 20px;
  color: black;
}

.hintGeneralSty {
  width: 400px;
  color: white;
}

.hintGeneralSty > span {
  display: block;
  padding: 5px 10px;
}

/* 提示信息 */
.hintMsg {
  background-color: #f39c12;
}

/* 警告 */
.warningMsg {
  background-color: red;
}

.successMsg {
  background-color: #27ae60;
}

.animationSty {
  animation: hintAnimation 1s ease alternate;
}

//动画
@keyframes hintAnimation {
  from {
    top: 10px;
  }
  to {
    top: 50%;
  }
}
</style>
三、简单分析

现在我们有了一个公共的组件和样式,那么我们就要想,要怎么实现再任意地方使用this. $xxx的方式调用呢。既然通过this. $xxx的方式调用那么我们会想到我们什么时候这样用过这种方式去调用呢?
vuex中我们使用过,全局事件总线我们也使用过。那么我们就必须利用一个vue的一个重要的内置关系VueComponent.prototype.proto===Vue.prototype
那么我就需要把我们组件的一些方法放到Vue的原型上边。只有这样才能实现全局通过this. $xxx的方式调用
使用过vuex的我们都知道,我们需要在创建vm之前使用Vue.use(vuex)的方式才能在全局使用,里边复杂的东西我也不太清楚,不过能确定的一点就是,他里边一定有一个Vue.prototype. $ xxx=xxx,还有就是vuex里边肯定有一个install函数,而且vue会自动调用这个函数

四、Vue.use之前的准备

既然知道了这些,我们就可以利用这些东西写一个配置文件,用于最后的Vue.use最后就能全局使用了,这个是js文件而非vue文件

// 引入弹窗组件
import myAlert from '../components/myComponent/myAlert'
//引入Vue
import Vue from 'vue'

// 创建组件构造函数
const hint = Vue.extend(myAlert)
// 注册全局组件
Vue.component('myHint', hint)
// 创建函数(传入配置对象)
const hintFn = function (options) {
    let data = {
        message: (options && options.message) || '提示信息',
        type: (options && options.type) || 2
    }
    if (options.type == 1) {
        data.title = (options && options.title) || '提示信息'
        data.sureMsg = (options && options.sureMsg) || '确定'
        data.cancelMsg = (options && options.cancelMsg) || '取消'
    }
    // 实例化组件对象
    const Instance = new hint({
        data: data
    })
    // 挂载
    let vm = Instance.$mount()
    // 添加到body里边
    document.body.appendChild(vm.$el)
    //调用的是弹窗组件的init方法
    return vm.init()
}
// 暴露
export default {
    install: (Vue) => {
    //这里写成$hintFn,是为了迎合vue的一些方法名
        Vue.prototype.$hintFn = hintFn // 挂到Vue的原型上使用
    }
}
五、main文件的配置及组件的使用

main.js文件的配置

import Vue from 'vue'
import App from './App.vue'
//引入上一步写的js文件
import myHint from './myAlert/index'
//使用
Vue.use(myHint)

new Vue({
  render: h => h(App),
   mounted() {
   //我们可以在这里输出的this的原型上看到我们自己定义的方法$hintFn
    console.log('this', this);
  },
}).$mount('#app')

组件的使用

<template>
  <div>
    <button @click="show">我是app的点击显示带交互的按钮</button>
    <button @click="show1">我是app的点击显示提示的按钮</button>
  </div>
</template>

<script>
export default {
  name: "App",
  methods: {
    show() {
      this.$hintFn({
        title: "测试",
        message: "第一次测试",
        type: 1,
      })
        .then(() => {
          console.log("点击了确定");
        })
        .catch(() => {
          console.log("点击了取消");
        });
    },
    show1() {
      this.$hintFn({
        type: 2,
        message: "测试",
      });
    },
  },
  mounted() {
  //我们同样可以看到组件原型的上有我们定义的$hintFn方法
    console.log("app组件中的this", this);
  },
};
</script>

<style>
</style>
六、总结

到这里封装的东西就结束了,第一次封装,还有很多不足,这里欢迎大佬们及时指正。
下周继续努力吧!!