以v-model与promise两种方式实现vue弹窗组件


Posted in Javascript onMay 21, 2018

最近公司有一个后台业务虽然也是写在了现有的后台系统中,但是之后要为这个业务单独拉出来新建一个后台系统,所以现有的后台系统中的vue组件库,就不能用了(因为不知道将来的系统要基于什么组件库,以防给未来移植项目带来麻烦),这次业务中又遇到了弹窗的功能,所以只能手动写一个了(虽然说弹窗组件很简单,也是想自己总结一下,有不对的地方也请指出),一开始用传统的props,$emit但是觉得要接两个取消与确认的回调这块的逻辑分散了所以就用了promise两个回调的方式把两个回调写在了一起,并不一定好,算是提供一种思路吧。

一.概览

先看最后的调用方式

props $emit方式

<chat-modal ref="chat-modal" v-model="showModal" cancelText="取消" sureText="确认" title="弹窗标题" small @on-ok="onOK" @on-cancel="onCancel">
  <div>slot的东西,想向弹窗中添加自定义的内容</div>
</chat-modal>
methods: {
  display() {
   this.showModal = true;//交互点击手动触发显示弹窗 
  },
  onOK() {},//点击确认的回调
  onCancel() {}//点击取消的回调
}

promise的回调方式

<chat-modal ref="chat-modal"></chat-modal>
methods: {
  display() {
    this.$refs["chat-modal"].openModal({
      title: "弹窗标题",
      sureText: "确认",
      cancelText: "取消"
    }).then(res => {
      //点击确认的回调
    }, res => {
      //点击取消的回调
    })
  }
}

第二种方式的好处就是把所有的逻辑都集中到了一个方法里。

二.看下组件的源码

tip: 样式有些烂...

<template>
  <div>
    <div class="shadow" v-show="showModal"></div>
    <div class="modal" :class="{'smSize': otherText.small || small}" v-show="showModal">
      <div class="header">{{ otherText.title || title}}</div>
      <div class="body">
        <slot></slot>
      </div>
      <div class="footer">
        <div class="item success" id="sure" ref="sure" @click="makeSure" v-show="otherText.sureText || sureText">{{ otherText.sureText || sureText }}</div>
        <div class="item red" id="cancel" ref="cancel" @click="makeCancel" v-show="otherText.cancelText || cancelText">{{ otherText.cancelText || cancelText }}</div>
      </div>
    </div>
  </div>
</template>
<script>
//此组件提供两种调用方法,可以在组件上v-model一个表示是否显示弹窗的对话框,然后需要的一些值通过props传入,然后$emit在组件上@监听做回调
//第二中方法所有的传值回调都只需要在组件内部的一个方法调用然后在组件外部this.$refs[xxx].open调用然后.then触发回调,比上一种方便些
var initOtherText = {
  sureText: "",
  cancelText: "",
  title: "",
  small: false
};
export default {
  props: {
    title: {
      type: String
    },
    sureText: {
      type: String
    },
    cancelText: {
      type: String
    },
    value: {
      type: Boolean
    },
    small: {
      type: Boolean
    }
  },
  watch: {
    value(newVal) {
      this.showModal = newVal;
    }
  },
  data() {
    return {
      otherText: JSON.parse(JSON.stringify(initOtherText)),
      showModal: this.value
    };
  },
  methods: {
    makeSure() {
      this.$emit("on-ok");
      this.$emit("input", false);
    },
    makeCancel() {
      this.$emit("on-cancel");
      this.$emit("input", false);
    },
    openModal(otherText) {
      this.otherText = { ...otherText };
      this.showModal = true;
      var pms = new Promise((resolve, reject) => {
        this.$refs["sure"].addEventListener("click", () => {
          this.showModal = false;
          resolve("点击了确定");
        });
        this.$refs["cancel"].addEventListener("click", () => {
          this.showModal = false;
          reject("点击了取消");
        });
      });
      return pms;
    }
  }
};
</script>
<style lang="scss" scoped>
.shadow {
  background-color: rgba(0, 0, 0, 0.5);
  display: table;
  height: 100%;
  left: 0;
  position: fixed;
  top: 0;
  transition: opacity 0.3s ease;
  width: 100%;
  z-index: 50;
}
.modal {
  display: table-cell;
  vertical-align: middle;
  overflow-x: hidden;
  position: fixed;
  background-color: white;
  box-shadow: rgba(0, 0, 0, 0.33) 0px 2px 8px;
  border-radius: 5px;
  outline: 0px;
  overflow: hidden;
  transition: all 0.3s ease;
  width: 600px;
  height: 400px;
  top: 50%;
  left: 50%;
  margin-top: -200px;
  margin-left: -300px;
}
.header {
  align-items: center;
  background-color: #62a39e;
  box-shadow: 0 1px 1px rgba(0, 0, 0, 0.16);
  color: #fff;
  font-weight: bold;
  display: -ms-flexbox;
  display: flex;
  height: 3.5rem;
  padding: 0 1.5rem;
  position: relative;
  z-index: 1;
}
.body {
  align-items: center;
  padding: 1.5rem;
}
.footer {
  justify-content: flex-end;
  padding: 1.5rem;
  position: absolute;
  bottom: 0;
  width: 100%;
  float: right;
}
.item {
  color: white;
  text-align: center;
  border-radius: 5px;
  padding: 10px;
  cursor: pointer;
  display: inline-block;
}
.info {
  background-color: #2196f3;
}
.success {
  background-color: #62a39e;
}
.red {
  background-color: #e95358;
}

.smSize {
  height: 200px;
}
</style>

首先分析一下第一种方式: 调用者需要在组件外部v-model上绑定一个变量(例中为showModal)来指示弹窗是否显示,显示的时候需要在组件外部手动设置 this.showModal = true ,组件内部props定义一个属性来接这个值为 value: {type: Boolean} ,同时在组件内部在用声明一个变量用来同步外部传进来的props值 默认值为 showModal: this.value (内部声明的值也叫了showModal),在watch中监听进行同步 watch: { value(newVal) { this.showModal = newVal } } ;然后把组件内部的这个showModal值绑定在需要显示或者隐藏的DOM元素上。向外抛出事件的时候是在点击组件内部的确定与关闭按钮时候

makeSure() {
      this.$emit("on-ok");
      this.$emit("input", false);
    },
makeCancel() {
      this.$emit("on-cancel");
      this.$emit("input", false);
    }

this.$emit("on-ok");this.$emit("on-cancel"); 这两句的是向外抛出事件在组件外部@接一下然后写自己需要的回调函数。这时就可以实现弹窗的显示与隐藏了,你可能发现并没有一句代码去设置this.showModal = false;弹窗就隐藏了。主要是因为这几句代码 v-model = 'showModal' 和 组件内部的 props: {value: {type: Boolean}} this.$emit("input", false) 。v-model其实是vue的语法糖, <chat-modal v-model="showModal"> 其实可以写为 <chat-modal :value="showModal" @input="showModal = arguments[0]"> 所以要求我们在组件内部必须规定props的名字必须为value, 然后在组件内部触发确定或者取消的时候在组件内部触发 this.$emit("input", false) 这样实现了直接隐藏弹窗而不必打扰用户让用户在组件外部在手动将showModal置为false.

然后来看promise的方式: 第一种方式传进来的值都通过props来接的,这种方式通过在组件内部定义了另一个对象来接传进来的值,

var initOtherText = {
  sureText: "",
  cancelText: "",
  title: "",
  small: false
};
otherText: JSON.parse(JSON.stringify(initOtherText)),

然后在menthods里定义了一个名为openModal的方法,然后把传进来的一系列参数赋值给组件内部的对象 this.otherText = { ...otherText }; this.showModal = true; 并且将showModal置为true,然后每次触发的时候新建一个promise对象,里面的异步事件为点击确定和取消的两个点击事件,这里要操作DOM了

this.$refs["sure"].addEventListener("click", () => {
  this.showModal = false;
  resolve("点击了确定");
});

获取确定按钮的DOM元素绑定点击事件,回调里将showModal置为false并且resolve,

this.$refs["cancel"].addEventListener("click", () => {
  this.showModal = false;
  reject("点击了取消");
});

获取取消按钮的DOM绑定点击事件,回调里reject.

遇到的坑

这之前遇到了一个坑,因为第一次已经绑定了点击事件,第二次resolve和reject就会失败,本想取消一下绑定事件,但是因为将整个弹窗v-show="showModal"的原因整个DOM被display:none;了就不需要手动解绑了。 第二个是关于用v-if还是v-show来隐藏弹窗,一开始用的是v-if但是发现在这步时

this.showModal = true;
var pms = new Promise((resolve, reject) => {
  this.$refs["sure"].addEventListener.xxx//省略
});
return pms;

将showModal置为true时然后就去绑定事件这时候还没有DOM还没有解析玩DOM树上还没有,要不就得用this.$nextTick增加了复杂度,最后采用了v-show;

关于优先级问题

如果既在组件上用prop传了值(title,sureText之类的)如 <chat-modal" title="xx" sureText="xxx"></chat-modal> 也在方法里传了

this.$refs["chat-modal"].openModal({
  title: "服务小结",
  sureText: "提交并结束",
  cancelText: "取消"
  }).then();

是以方法的优先级为高,在组件内部DOM元素上通过||设置了优先级,比如 <div class="header popModal">{{ otherText.title || title}}</div> 有方法的值取方法的值,没有取props得值。

总结

以上所述是小编给大家介绍的以v-model与promise两种方式实现vue弹窗组件,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

Javascript 相关文章推荐
jquery 插件开发方法小结
Oct 23 Javascript
解决jquery的.animate()函数在IE6下的问题
Dec 03 Javascript
js网页右下角提示框实例
Oct 14 Javascript
深入理解JavaScript系列(50):Function模式(下篇)
Mar 04 Javascript
jQuery实现的左右移动焦点图效果
Jan 14 Javascript
jQuery中$.each()函数的用法引申实例
May 12 Javascript
ECMAScript6轮播图实践知识总结
Aug 17 Javascript
js动态生成form 并用ajax方式提交的实现方法
Sep 09 Javascript
使用AngularJS对表单提交内容进行验证的操作方法
Jul 12 Javascript
解决vuecli3.0热更新失效的问题
Sep 19 Javascript
微信小程序开发之tabbar图标和颜色的实现
Oct 17 Javascript
JavaScript基于遍历操作实现对象深拷贝功能示例
Mar 05 Javascript
Vue二次封装axios为插件使用详解
May 21 #Javascript
详解vue的diff算法原理
May 20 #Javascript
详解使用vue-admin-template的优化历程
May 20 #Javascript
vuex进阶知识点巩固
May 20 #Javascript
简单的三步vuex入门
May 20 #Javascript
vue项目如何刷新当前页面的方法
May 18 #Javascript
原生JS实现的碰撞检测功能示例
May 18 #Javascript
You might like
浅析PHP中Collection 类的设计
2013/06/21 PHP
(PHP实现)只使用++运算实现加法,减法,乘法,除法
2013/06/27 PHP
PHP静态成员变量和非静态成员变量详解
2017/02/14 PHP
详细解读php的命名空间(二)
2018/02/21 PHP
php基于 swoole 实现的异步处理任务功能示例
2019/08/13 PHP
JavaScript 异步调用框架 (Part 4 - 链式调用)
2009/08/04 Javascript
js下判断 iframe 是否加载完成的完美方法
2010/10/26 Javascript
jQuery学习笔记[1] jQuery中的DOM操作
2010/12/03 Javascript
jQuery-Easyui 1.2 实现多层菜单效果的代码
2012/01/13 Javascript
关于jQuery对象数据缓存Cache原理以及jQuery.data详解
2013/04/07 Javascript
javascript实现相同事件名称,不同命名空间的调用方法
2015/06/26 Javascript
jquery判断密码强度的验证代码
2020/04/22 Javascript
js实现的页面矩阵图形变换特效
2016/01/26 Javascript
jQuery实现div拖拽效果实例分析
2016/02/20 Javascript
Javascript中内建函数reduce的应用详解
2016/10/20 Javascript
js获取指定时间的前几秒
2017/04/05 Javascript
Angular中自定义Debounce Click指令防止重复点击
2017/07/26 Javascript
JS实现点击链接切换显示隐藏内容的方法
2017/10/19 Javascript
JavaScript实现区块链
2018/03/14 Javascript
vue 实现的树形菜的实例代码
2018/03/19 Javascript
11个教程中不常被提及的JavaScript小技巧(推荐)
2019/04/17 Javascript
微信小程序登录对接Django后端实现JWT方式验证登录详解
2019/07/29 Javascript
VSCode插件安装完成后的配置(常用配置)
2020/08/24 Javascript
[03:49]2016完美“圣”典风云人物:AMS专访
2016/12/06 DOTA
基于scrapy实现的简单蜘蛛采集程序
2015/04/17 Python
python urllib urlopen()对象方法/代理的补充说明
2017/06/29 Python
python:print格式化输出到文件的实例
2018/05/14 Python
Python3中详解fabfile的编写
2018/06/24 Python
基于 Django 的手机管理系统实现过程详解
2019/08/16 Python
Python使用urllib模块对URL网址中的中文编码与解码实例详解
2020/02/18 Python
纯CSS3+DIV实现小三角形边框效果的示例代码
2020/08/03 HTML / CSS
Molly Bracken法国电子商店:法国女性时尚品牌
2019/07/24 全球购物
全球最受追捧的运动服品牌领先数字目的地:Stylerunner
2020/11/25 全球购物
财务简历的自我评价
2014/03/05 职场文书
QQ空间主人寄语大全
2014/04/12 职场文书
青岛市的收音机研制与生产
2022/04/07 无线电