以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 相关文章推荐
js判断是否为数组的函数: isArray()
Oct 30 Javascript
jquery移动listbox的值原理及代码
May 03 Javascript
jquery使用jquery.zclip插件复制对象的实例教程
Dec 04 Javascript
js中创建对象的几种方式示例介绍
Jan 26 Javascript
使用VS开发 Node.js指南
Jan 06 Javascript
js简单判断移动端系统的方法
Feb 25 Javascript
javascript基础知识讲解
Jan 11 Javascript
zTree树形插件异步加载方法详解
Jun 14 Javascript
vue 根据数组中某一项的值进行排序的方法
Aug 30 Javascript
如何获取vue单文件自身源码路径
May 06 Javascript
vue2配置scss的方法步骤
Jun 06 Javascript
关于AngularJS中几种Providers的区别总结
May 17 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
第五节 克隆 [5]
2006/10/09 PHP
php自定义的格式化时间示例代码
2013/12/05 PHP
php生成4位数字验证码的实现代码
2015/11/23 PHP
Yii1.1中通过Sql查询进行的分页操作方法
2017/03/16 PHP
laravel 解决crontab不执行的问题
2019/10/22 PHP
jquery 之 $().hover(func1, funct2)使用方法
2012/06/14 Javascript
jquery1.83 之前所有与异步列队相关的模块详细介绍
2012/11/13 Javascript
简单的代码实现jquery定时器
2013/11/17 Javascript
JS实现的数组全排列输出算法
2015/03/19 Javascript
JS实现简单路由器功能的方法
2015/05/27 Javascript
JavaScript实现获取某个元素相邻兄弟节点的prev与next方法
2016/01/25 Javascript
jQuery 获取页面li数组并删除不在数组中的key
2016/08/02 Javascript
js 原型对象和原型链理解
2017/02/09 Javascript
webpack下实现动态引入文件方法
2018/02/22 Javascript
详解Vue Elememt-UI构建管理后台
2018/02/27 Javascript
JavaScript基础心法 深浅拷贝(浅拷贝和深拷贝)
2018/03/05 Javascript
Element-ui之ElScrollBar组件滚动条的使用方法
2018/09/14 Javascript
浅谈Angular7 项目开发总结
2018/12/19 Javascript
vue路由守卫及路由守卫无限循环问题详析
2019/09/05 Javascript
vuex实现购物车功能
2020/06/28 Javascript
在Vue 中获取下拉框的文本及选项值操作
2020/08/13 Javascript
Python调用C/C++动态链接库的方法详解
2014/07/22 Python
Python实现多级目录压缩与解压文件的方法
2018/09/01 Python
使用matplotlib绘制图例标签中带有公式的图
2019/12/13 Python
Python定时任务APScheduler安装及使用解析
2020/08/07 Python
PyCharm+Miniconda3安装配置教程详解
2021/02/16 Python
新西兰珠宝品牌:Michael Hill
2017/09/16 全球购物
Linux管理员面试经常问道的相关命令
2013/04/29 面试题
主要的Ajax框架都有什么
2013/11/14 面试题
实习自我鉴定
2013/12/15 职场文书
旅游个人求职信范文
2014/01/30 职场文书
七年级历史教学反思
2014/02/05 职场文书
蛋糕店创业计划书
2014/05/06 职场文书
企业安全生产承诺书
2014/05/22 职场文书
公务员保密工作承诺书
2015/05/04 职场文书
SQL Server2019安装的详细步骤实战记录(亲测可用)
2022/06/10 SQL Server