从零开始实现Vue简单的Toast插件


Posted in Javascript onDecember 03, 2018

前言

一直都觉得vue的插件生涩难懂,但是又很好奇,在看了几篇文章,试着写了写之后觉得也没那么难,本文主要实现一个简单的Toast插件,方便迁移到不同的项目中,用来全局提示、警告一些信息。

概述:

在前端项目中,有时会需要通知、提示一些信息给用户,尤其是在后台系统中,操作的正确与否,都需要给与用户一些信息。

1. 实例

在Vue组件的methods内,调用如下代码

this.$toast({
 content: "可自动关闭",
 autoClose: true
})

在页面的右侧会出现一个Toast弹框,多次点击时,会依照顺序进行显示,并且Toast可自动关闭,具体效果如图。

从零开始实现Vue简单的Toast插件

代码地址:Github UI-Library

2. 原理

代码结构

将Toast插件分为两个部分:

  • Toast组件本身,包含本身的dom结构以及data,并在其生命周期完成在页面中的挂载与销毁;
  • 在组件外构建一层代理并提供相关方法用于调用、并控制多个Toast在页面中显示的顺序。

Toast方法

为了能够通过this.$toast({...})调用Toast组件,须在Vue的prototype上添加一个方法,如下

let instances = []
let initIndex = 0
Vue.prototype.$toast = (options = {}) => {
 /* 创建一个Toast组件的实例 */
 let instance = generateInstance(options)
 /* 将单个toast存入队列中 */ 
 instances.push(instance)
}

当调用该方法时,通过generateInstance创建一个Toast组件的实例,并将其放入instances,统一管理。

/* 构造单个toast */
const ToastConstructor = Vue.extend(Toast)
const verticalOffset = 16
function generateInstance(options) {
 // 利用ToastConstructor创建一个Toast的实例
 let instance = new ToastConstructor({
 propsData: options
 }).$mount(document.createElement('div'))
 if (typeof options.onClose === 'function') instance.onClose = options.onClose
 //计算instance verticalOffset
 let id = 'toast_' + initIndex++
 instance.id = id
 // 初始化Toast在空间中的垂直偏移量
 instance.verticalOffset = initVerticalOffset(instance.position)
 //监听组件close
 instance.$once('toastClose', function () {
 const curInstance = this
 // 当Toast组件关闭时,重新计算垂直方向的偏移量
 updateVerticalOffset(curInstance)
 typeof curInstance.onClose === 'function' && curInstance.onClose()
 })
 return instance;
}

generateInstance函数中,首先利用ToastConstructor构造函数创建一个Toast组件的实例,并通过propsData传入属性值,同时通过$mount(document.createElement('div'))渲染该组件。

ToastConstructor是通过Vue.extend创造Toast组件的构造函数,关于这部分的具体原理,可以参考
基于Vue构造器创建Form组件的通用解决方案。

/* 初始化每个toast对象在页面中的垂直属性 */
function initVerticalOffset(position) {
 // 筛选同一方向的Toast组件
 let typeInstances = instances.filter(item => item.position === position)
 // 计算偏移量
 return typeInstances.reduce((sum, elem) => 
  (elem.$el.offsetHeight + sum + verticalOffset), 
  verticalOffset)
}

之后当某个Toast关闭时,需要更新所有Toast实例在页面中偏移量

/* 更新每个toast对象在页面中的垂直属性 */
function updateVerticalOffset(removeInstance) {
 let index = 0
 let removeHeight = removeInstance.verticalOffset
 instances.find((elem, i) => {
 if (elem.id === removeInstance.id) index = i
 })
 // 删除关闭的Toast组件
 instances.splice(index, 1)
 // 更新在删除位置之后的组件的位置
 for (; index < instances.length; ++index) {
 if (instances[index].position === removeInstance.position) {
  [instances[index].verticalOffset, removeHeight] = 
  [removeHeight, instances[index].verticalOffset]
 }
 }
}

以上完成了Toast组件的创建、如何在页面中初始化、更新的位置。

Toast组件

组件的功能比较简单,只需要展示信息,以及具备自动关闭、手动关闭两个功能,属性也要包括Toast的类型、位置、内容等。

组件的生命周期

let instance = new ToastConstructor({
 propsData: options
}).$mount(document.createElement('div'))

当Toast组件$mount调用时,会触发mounted的生命周期

mounted() {
 // 挂载Toast在页面中
 document.body.appendChild(this.$el);
 // 需要自动关闭时,调用startTimer
 if (this.autoClose) this.startTimer();
},
beforeDestroy() {
 this.stopTimer();
 this.$el.removeEventListener("transitionend", this.destroyElement);
},
destroyed() {
 // 注销
 this.$el.parentNode.removeChild(this.$el);
}

自动关闭

需要自动时,就要在利用setTimeout完成该功能,并在注销时clearTimeout掉,防止泄露。

startTimer() {
 if (this.duration > 0) {
 this.timer = setTimeout(() => {
  if (!this.closed) {
  this.close();
  }
 }, this.duration);
 }
},
stopTimer() {
 if (this.timer) clearTimeout(this.timer);
}

3. 使用

进一步将其封装成Vue的插件

export default {
 install (Vue) {
 Vue.prototype.$toast = (options = {}) => {...}
 }
}

并且对所需要传入的必需属性,做处理异常处理

export default {
 install (Vue) {
 Vue.prototype.$toast = (options = {}) => {...}
 }
}

4. 总结
通过封装一个Toast插件,提取不同业务之间公共的部分,减少业务的工作量。

好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对三水点靠木的支持。

Javascript 相关文章推荐
javascript 一段左右两边随屏滚动的代码
Jun 18 Javascript
浅谈javascript语法和定时函数
May 03 Javascript
javascript实现表格增删改操作实例详解
May 15 Javascript
全面了解addEventListener和on的区别
Jul 14 Javascript
浅谈jQuery中事情的动态绑定
Feb 12 Javascript
从零学习node.js之详解异步控制工具async(八)
Feb 27 Javascript
详解vue.js全局组件和局部组件
Apr 10 Javascript
jQuery选择器之子元素过滤选择器
Sep 28 jQuery
Vue使用高德地图搭建实时公交应用功能(地图 + 附近站点+线路详情 + 输入提示+换乘详情)
May 16 Javascript
redux.js详解及基本使用
May 24 Javascript
如何用webpack4.0撸单页/多页脚手架 (jquery, react, vue, typescript)
Jun 18 jQuery
初试vue-cli使用HBuilderx打包app的坑
Jul 17 Javascript
使用NestJS开发Node.js应用的方法
Dec 03 #Javascript
写gulp遇到的ES6问题详解
Dec 03 #Javascript
使用mpvue搭建一个初始小程序及项目配置方法
Dec 03 #Javascript
JS基于Location实现访问Url、重定向及刷新页面的方法分析
Dec 03 #Javascript
koa2实现登录注册功能的示例代码
Dec 03 #Javascript
react-router 路由切换动画的实现示例
Dec 03 #Javascript
Vue.js 中的 v-model 指令及绑定表单元素的方法
Dec 03 #Javascript
You might like
PHP的Yii框架的常用日志操作总结
2015/12/08 PHP
thinkPHP数据库增删改查操作方法实例详解
2016/12/06 PHP
利用PHPStorm如何开发Laravel应用详解
2017/08/30 PHP
iframe 父窗口和子窗口相互的调用方法集锦
2010/12/15 Javascript
js 处理数组重复元素示例代码
2013/12/27 Javascript
理解JavaScript事件对象
2016/01/25 Javascript
vue+webpack 打包文件 404 页面空白的解决方法
2018/02/28 Javascript
微信小程序自定义toast的实现代码
2018/11/16 Javascript
Vue js 的生命周期(看了就懂)(推荐)
2019/03/29 Javascript
JavaScript强制类型转换和隐式类型转换操作示例
2019/05/01 Javascript
谈谈我在vue-cli3中用预渲染遇到的坑
2020/04/22 Javascript
详解Node.js使用token进行认证的简单示例
2020/05/25 Javascript
详解JS深拷贝与浅拷贝
2020/08/04 Javascript
vue 使用微信jssdk,调用微信相册上传图片功能
2020/11/13 Javascript
解决Vue-cli3没有vue.config.js文件夹及配置vue项目域名的问题
2020/12/04 Vue.js
[03:04]2018年度DOTA2玩家最喜爱的主播-完美盛典
2018/12/16 DOTA
python虚拟环境virtualenv的使用教程
2017/10/20 Python
python批量从es取数据的方法(文档数超过10000)
2018/12/27 Python
在Pycharm terminal中字体大小设置的方法
2019/01/16 Python
python爬取网易云音乐热歌榜实例代码
2020/08/07 Python
numba提升python运行速度的实例方法
2021/01/25 Python
如何在Canvas上的图形/图像绑定事件监听的实现
2020/09/16 HTML / CSS
英国高档时尚男装购物网站:MR PORTER
2016/08/09 全球购物
Marriott国际:万豪国际酒店查询预订
2017/09/25 全球购物
IRO美国官网:法国服装品牌
2018/03/06 全球购物
生产内勤岗位职责
2013/12/07 职场文书
暑期社会实践学生的自我评价
2014/01/09 职场文书
大学生咖啡店创业计划书
2014/01/21 职场文书
生产部管理制度
2014/01/31 职场文书
电视节目策划方案
2014/05/16 职场文书
刑事辩护授权委托书格式
2014/10/13 职场文书
学生党员批评与自我批评
2014/10/15 职场文书
2014矛盾纠纷排查调处工作总结
2014/12/09 职场文书
社区六一儿童节活动总结
2015/02/11 职场文书
筑梦中国心得体会
2016/01/18 职场文书
详解CSS伪元素的妙用单标签之美
2021/05/25 HTML / CSS