以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 相关文章推荐
一个对于Array的简单扩展
Oct 03 Javascript
extjs 3.31 TreeGrid实现静态页面加载json到TreeGrid里面
Apr 02 Javascript
jquery的map与get方法详解
Nov 04 Javascript
JavaScript 学习笔记之语句
Jan 14 Javascript
javascript函数式编程程序员的工具集
Oct 11 Javascript
jQuery延迟执行的实现方法
Dec 21 Javascript
vue表单绑定实现多选框和下拉列表的实例
Aug 12 Javascript
JS实现div模块的截图并下载功能
Oct 17 Javascript
基于vue实现网站前台的权限管理(前后端分离实践)
Jan 13 Javascript
ES6 系列之 WeakMap的使用示例
Aug 06 Javascript
详解小程序云开发攻略(解决最棘手的问题)
Sep 30 Javascript
公众号SVG动画交互实战代码
May 31 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
simplehtmldom Doc api帮助文档
2012/03/26 PHP
php使用正则表达式提取字符串中尖括号、小括号、中括号、大括号中的字符串
2020/04/05 PHP
本地计算机无法启动Apache故障处理
2014/08/08 PHP
如何在centos8自定义目录安装php7.3
2019/11/28 PHP
javascript时区函数介绍
2012/09/14 Javascript
对table和ul实现js分页示例分享
2014/02/24 Javascript
node.js中的querystring.unescape方法使用说明
2014/12/10 Javascript
js获取时间并实现字符串和时间戳之间的转换
2015/01/05 Javascript
cookie的secure属性详解
2015/04/08 Javascript
javascript动态设置样式style实例分析
2015/05/13 Javascript
JavaScript阻止回车提交表单的方法
2015/12/30 Javascript
Node.js的Web模板引擎ejs的入门使用教程
2016/06/06 Javascript
玩转JavaScript OOP - 类的实现详解
2016/06/08 Javascript
使用prop解决一个checkbox选中后再次选中失效的问题
2017/07/05 Javascript
jQuery实现浏览器之间跳转并传递参数功能【支持中文字符】
2018/03/28 jQuery
浅谈Vue路由快照实现思路及其问题
2018/06/07 Javascript
vue生成token并保存到本地存储中
2018/07/17 Javascript
浅谈vue项目,访问路径#号的问题
2020/08/14 Javascript
Javascript基于OOP实实现探测器功能代码实例
2020/08/26 Javascript
Python查找文件中包含中文的行方法
2018/12/19 Python
详解Python做一个名片管理系统
2019/03/14 Python
anaconda如何查看并管理python环境
2019/07/05 Python
Python中利用LSTM模型进行时间序列预测分析的实现
2019/07/26 Python
python修改FTP服务器上的文件名
2019/09/11 Python
pytorch 批次遍历数据集打印数据的例子
2019/12/30 Python
python怎么调用自己的函数
2020/07/01 Python
如何利用Python写个坦克大战
2020/11/18 Python
CSS3实现菜单悬停效果
2020/11/17 HTML / CSS
HTML5 embed 标签使用方法介绍
2013/08/13 HTML / CSS
优秀研究生自我鉴定
2013/12/04 职场文书
时尚休闲吧创业计划书
2014/01/25 职场文书
沈阳故宫导游词
2015/01/31 职场文书
2015年安全教育月活动总结
2015/03/26 职场文书
2015年司法所工作总结
2015/04/27 职场文书
中学教代会开幕词
2016/03/04 职场文书
MySQL自定义函数及触发器
2022/08/05 MySQL