vue.extend()+aysnc封装一个弹层组件【vue2】

12880次阅读 485人点赞 作者: WuBin 发布时间: 2021-04-29 16:48:46
扫码到手机查看

介绍

通过这篇文章,你将掌握:

  • vue.extend
  • async + await在vue中的使用
  • $mount动态将组件挂载到body下
  • 收获一个支持vue2的弹层组件(vue3由于更换api,需要修改,本文暂不进行说明)。

封装弹层组件

首先要封装一个弹层组件confirm.vue

// 模板
<template>
  <transition name="confirm-fade">
    <div class="cofirm" v-if="show">
      <div class="cofirm-inner" @click.stop>
        <div class="head">
          <p class="head-text"><em>{{content.headText ? content.headText : '系统提示'}}</em></p>
          <div class="close" @click="cancel">×</div>
        </div>
        <div class="body">
          <div class="content">{{content.title}}</div>
          <div class="tips-btn">
            <button
                type="button"
                v-if="content.confirm"
                @click="confirm"
                class="btn confirm"
            >
              {{content.confirm}}
            </button>
            <button type="button"
                    v-if="content.cancel"
                    @click="cancel"
                    class="btn"
            >{{content.cancel}}</button>
          </div>
        </div>
      </div>
    </div>
  </transition>
</template>
// JS
export default {
  data(){
    return {
      // 决定是否显示
      show :false,
      // 弹窗的内容
      content: {
        headText: '',
        title:'',
        cancel:'',
        confirm:''
      }
    }
  },
  methods:{
    confirm(){
      return true;
    },
    cancel(){
      return false;
    }
  }
};
<style lang="less" rel="stylesheet/less" scoped>
  @confirm-dialog-bg: rgba(46, 62, 78, 0.85);
  @confirm-gray-lite: #e6e6e6;
  @confirm-brand: #518ACA;
  @confirm-gray-dark: #4d4d4d;
  @confirm-gray-middle: #dedede;

  @keyframes confirmInDown {
    0% {
      opacity: 0;
      transform: translate(-50%, -80%);
    }
    100% {
      opacity: 1;
      transform: translate(-50%, -50%);
    }
  }

  .cofirm{
    background-color: @confirm-dialog-bg;
    position: fixed;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    z-index: 9999;
    user-select: none;
  }
  .confirm-fade-enter-active,
  .confirm-fade-leave-active {
    transition: opacity 0.5s ease;
  }
  .confirm-fade-enter, .confirm-fade-leave-to {
    opacity: 0;
  }
  .cofirm-inner {
    position: absolute;
    background-color: #fff;
    overflow: hidden;
    border-radius: 5px;
    box-shadow: 0 3px 3px rgba(0,0,0, 0.4);
    width: 360px;
    left: 50%;
    top: 50%;
    animation: confirmInDown 0.4s both;
    .head{
      display: flex;
      justify-content: space-between;
      border-bottom: 1px solid @confirm-gray-lite;
      height: 40px;
      padding: 0 15px;
      font-size: 14px;
      line-height: 40px;
      color: darken(@confirm-gray-lite, 30%);

      .head-text{
        font-size: 0;
        em{
          font-size: 14px;
          display: inline-block;
        }
      }
      .head-text::before{
        content: "!";
        display: inline-block;
        font-size: 12px;
        border: 1px solid;
        border-radius: 50%;
        vertical-align: 2px;
        width: 14px;
        height: 14px;
        line-height: 14px;
        margin-right: 5px;
        text-align: center;
      }
      .close {
        text-align: right;
        font-size: 20px;
        color: darken(@confirm-gray-lite, 20%);
        cursor: pointer;
        &:hover{
          color: @confirm-brand;
        }
      }
    }

    .body{
      padding: 15px;

      .content {
        font-size: 16px;
        color: @confirm-gray-dark;
        letter-spacing: 0;
        line-height: 24px;
        text-align: left;
        padding: 10px 0 25px;
      }

      .tips-btn{
        display: flex;
        width: 100%;
        justify-content: flex-end;
        align-items: center;
        .btn{
          padding: 0 22px ;
          height: 36px;
          line-height: 36px;
          border-radius: 6px;
          background-color: #fff;
          border: 1px solid @confirm-gray-middle;
          &:hover{
            background-color: @confirm-gray-lite;
          }
          & + .btn{
            margin-left: 10px;
          }
          &.confirm{
            background-color: @confirm-brand;
            color: #fff;
            border: 1px solid @confirm-brand;
            &:hover{
              background-color: darken(@confirm-brand, 10%);
            }
          }
        }
      }

    }
  }
</style>

编写引用的文件(重点)

以我的目录为例,在src/common/js/confirm.js,新建js文件

先引入vue,将组件添加进Vue中

import Vue from 'vue';
// 引用组件
import Confirm from 'components/confirm/confirm';
// 使用基础 Vue 构造器,创建一个“子类”。
// 参数是一个包含组件选项的对象。
const confirmConstructor = Vue.extend(Confirm);

将Confirm组件创建为一个子类,如果直接挂在的话,执行以下代码会直接挂载到<div id="demo"></div>

new confirmConstructor().$mount('#demo');

如果newconfirmConstructor().$mount(); 那么就不会直接挂载在DOM上,而是先实例化成一个组件对象。($mount()为手动挂载 带元素id为挂载到指定元素上,不加元素不渲染, 只是实例化该组件

将点击交互封装为Promise

let confirmer = function (msg) {
  return  new Promise((resolve, reject) => {
    // 將组件实例化
    let confirmEl = new confirmConstructor().$mount();
    // 通过confirmEl.$el获取组件的DOM元素 并插入到body下
    document.body.appendChild(confirmEl.$el);
    // 如果每一项都要设置就使用这种方式,传值的时候就传一个对象
    // confirmEl.content = content;
    // 给组件的data->content属性进行赋值
    confirmEl.content = {
      title: msg,
      confirm: '确定',
      cancel: '取消'
    };
    confirmEl.show = true;
    // 监听确定的事件
    confirmEl.confirm = function () {
      resolve(true);
      confirmEl.show = false;
    };
    // 接收取消的事件
    confirmEl.cancel = function () {
      reject(false);
      confirmEl.show = false;
    };
  });
};

这里需要特别注意的是,需要将点击事件,作为一个promise对象返回。这样就可以使用.then()进行链式调用和使用async+awit将这个“异步”交互转为同步!

在这里,$mount还有另外一种写法(他们的作用都是相同的):

let confirmEl = new confirmConstructor().$mount();
// 等价于
let confirmEl = new confirmConstructor({
   //实例化后要绑定的元素
   el:  document.createElement('div')
});
// 如官网案例中new Vue时 
new Vue({ el: '#app', data: {...}}) 
// 等同于
new Vue({ data: {...} }).$mount('#app')

async+await同步异步操作

async function confirmChoose(content) {
  let sure = false;
  try {
    // await后面是一个promise对象
    sure =  await confirmer(content);
  } catch (e) {
    // 捕获到错误 转为立即resolve的promise对象
    sure = false;
  }
  return sure;
}

使用async将异步操作阻断,等待(await)操作执行。注意 async与await必须成对出现!!

封装alert作用的组件

// 这个仅仅做提示用,无需根据返回值判断
let alertChoose = function(msg) {
  let confirmEl = new confirmConstructor({
    // 实例化后要绑定的元素
    el: document.createElement('div')
  });
  document.body.appendChild(confirmEl.$el);
  confirmEl.show = true;
  confirmEl.content.title = msg;
  confirmEl.content.confirm = '确定';
  confirmEl.confirm = function () {
    confirmEl.show = false;
  };
  confirmEl.cancel = function () {
    confirmEl.show = false;
  };
};

因为alert作用的弹层,无需根据点击按钮是哪个进行判断,只要点击,就隐藏弹层就行了。所以只改变show的状态即可。

完整代码

import Vue from 'vue';
import Confirm from 'components/confirm/confirm';

const confirmConstructor = Vue.extend(Confirm);

let confirmer = function (msg) {
  return  new Promise((resolve, reject) => {
    let confirmEl = new confirmConstructor().$mount();
    document.body.appendChild(confirmEl.$el);
    confirmEl.content = {
      title: msg,
      confirm: '确定',
      cancel: '取消'
    };
    confirmEl.show = true;
    confirmEl.confirm = function () {
      resolve(true);
      confirmEl.show = false;
    };
    confirmEl.cancel = function () {
      reject(false);
      confirmEl.show = false;
    };
  });
};

let alertChoose = function(msg) {
  let confirmEl = new confirmConstructor({
    el: document.createElement('div')
  });
  document.body.appendChild(confirmEl.$el);
  confirmEl.show = true;
  confirmEl.content.title = msg;
  confirmEl.content.confirm = '确定';
  confirmEl.confirm = function () {
    confirmEl.show = false;
  };
  confirmEl.cancel = function () {
    confirmEl.show = false;
  };
};

async function confirmChoose(content) {
  let sure = false;
  try {
    sure =  await confirmer(content);
  } catch (e) {
    sure = false;
  }
  return sure;
}
export { confirmChoose, alertChoose };

在main.js中引用

// 引入封装的方法
import { confirmChoose, alertChoose } from "common/js/confirm";
// 将方法挂载到vue的原型链上 使用:this.$confirm(msg)
Vue.prototype.$confirm = confirmChoose;
// 挂载到window上,直接覆盖原来的方法 使用 window.alert(msg)
window.alert = alertChoose;
// 可以先保存旧的弹窗备用
window.alertOld = window.alert;
// 再将原本的弹窗进行覆盖
window.alert = alertChoose;

如何使用

使用单按钮弹层

使用单按钮弹层,最简单不过了,直接在点击的时候执行window.alert('我的信息')即可。如

if (this.arr.includes(url)) {
        window.alert('抱歉,请不要提交重复的视频地址!');
        return;
}

使用询问框弹层

使用的时候有两种方式,先说我推荐的使用方式吧。

使用async+await

前面说过了,使用async+await必须成对出现!如在一个组件中,执行点击删除,出现询问框的操作。

methods: {
   async deleteResourceItem(url, index, type) {
       let sure = await this.$confirm('确定要删除当前资源吗?');
       if (!sure) {
           return false;
        }
        ...
   }
} 

需要在函数的名称前面加上async!!他的作用就是告诉浏览器,这里面有个异步操作,需要转为同步!

然后在返回promise的异步操作前面加上await将异步转为同步操作!

这样就可以根据点击的按钮接收到返回的值了。

还有一些操作是在异步操作之中使用async的情况,来看一个例子:

methods: {
  getDetailInfo() {
    getInfo(url).then(async res => {
        // 判断是否是本站的链接
        if (istrue) {
          let sure = await this.$confirm(msg);
          if (sure) {
            this.dosomething();
          }
        } else {
          window.alert(...);
        }
    }).catch(err => {
        window.alert(....);
    }).finally(() => {
        // 始终都执行
    });
  }
}
async+await的使用原则就是就近原则!哪个函数里面有异步操作需要转化为同步的,就在哪个函数外面加async,等待哪个异步操作,就在操作之前加await。如果遇到报错:await is a reserved word,是因为async没放到应用await的函数上,async和await是成对出现的,就算匿名函数也要加上。加上async的函数会被await阻塞,await会跳出async让出线程。

使用then

this.$confirm('确定要删除当前资源吗?')
    .then(res => {
        // ..
    }).catch(rej => {
        // .. 捕获promise reject的错误
    });

附件下载

相关资料

点赞 支持一下 觉得不错?客官您就稍微鼓励一下吧!
关键词:vue.extend,$mount
推荐阅读
  • uniapp实现被浏览器唤起的功能

    当用户打开h5链接时候,点击打开app若用户在已经安装过app的情况下直接打开app,若未安装过跳到应用市场下载安装这个功能在实现上主要分为两种场景,从普通浏览器唤醒以及从微信唤醒。

    10049次阅读 659人点赞 发布时间: 2022-12-14 16:34:53 立即查看
  • Vue

    盘点Vue2和Vue3的10种组件通信方式

    Vue中组件通信方式有很多,其中Vue2和Vue3实现起来也会有很多差异;本文将通过选项式API组合式API以及setup三种不同实现方式全面介绍Vue2和Vue3的组件通信方式。

    4677次阅读 346人点赞 发布时间: 2022-08-19 09:40:16 立即查看
  • JS

    几个高级前端常用的API

    推荐4个前端开发中常用的高端API,分别是MutationObserver、IntersectionObserver、getComputedstyle、getBoundingClientRect、requ...

    14754次阅读 967人点赞 发布时间: 2021-11-11 09:39:54 立即查看
  • PHP

    【正则】一些常用的正则表达式总结

    在日常开发中,正则表达式是非常有用的,正则表达式在每个语言中都是可以使用的,他就跟JSON一样,是通用的。了解一些常用的正则表达式,能大大提高你的工作效率。

    13961次阅读 525人点赞 发布时间: 2021-10-09 15:58:58 立即查看
  • 【中文】免费可商用字体下载与考证

    65款免费、可商用、无任何限制中文字体打包下载,这些字体都是经过长期验证,经得住市场考验的,让您规避被无良厂商起诉的风险。

    12705次阅读 1023人点赞 发布时间: 2021-07-05 15:28:45 立即查看
  • Vue

    Vue3开发一个v-loading的自定义指令

    在vue3中实现一个自定义的指令,有助于我们简化开发,简化复用,通过一个指令的调用即可实现一些可高度复用的交互。

    16940次阅读 1357人点赞 发布时间: 2021-07-02 15:58:35 立即查看
  • JS

    关于手机上滚动穿透问题的解决

    当页面出现浮层的时候,滑动浮层的内容,正常情况下预期应该是浮层下边的内容不会滚动;然而事实并非如此。在PC上使用css即可解决,但是在手机端,情况就变的比较复杂,就需要禁止触摸事件才可以。

    15465次阅读 1257人点赞 发布时间: 2021-05-31 09:25:50 立即查看
  • Vue

    Vue+html2canvas截图空白的问题

    在使用vue做信网单页专题时,有海报生成的功能,这里推荐2个插件:一个是html2canvas,构造好DOM然后转canvas进行截图;另外使用vue-canvas-poster(这个截止到2021年3月...

    30556次阅读 2403人点赞 发布时间: 2021-03-02 09:04:51 立即查看
  • Vue

    vue-router4过度动画无效解决方案

    在初次使用vue3+vue-router4时候,先后遇到了过度动画transition进入和退出分别无效的情况,搜遍百度没没找到合适解决方法,包括vue-route4有一些API都进行了变化,以前的一些操...

    26527次阅读 2041人点赞 发布时间: 2021-02-23 13:37:20 立即查看
交流 收藏 目录