Vue.js实现一个漂亮、灵活、可复用的提示组件示例


Posted in Javascript onMarch 17, 2017

这次的教程里,我们要把组件化进行到底!最近半年的几个项目中,都遇到了需要使用Toast或者Notification组件的情况。在目前已有的一些基于Vue.js开发的组件库,都没有找到太合适的,所以自己重头实现了一个。历经几个项目的磨练,这个提示组件的功能已经越来越完善,这次就分享一下组件以及其实现思路吧。

Vue.js实现一个漂亮、灵活、可复用的提示组件示例

深入组件化,组件的拆分、整合与复用

Vue.js的组件化可以说是其招牌特性之一,而在实际应用时,并非一味地追求组件颗粒越小越好,而是需要根据项目的实际需求,来分析自己需要什么级别的组件。

例如在一个SPA中,我可能有主页、文章列表页、文章页、个人中心页4个主要的视图,于是我将其分别对应的写成4个组件。

但是在实际编写的过程中,发现他们共用了同一套侧边栏,而侧边栏对应的代码也在4个组件中重复书写了4次。所以可以将侧边栏单独写成一个组件进行复用。

之后,我们可能发现可以复用的还有一些表单、按钮之类的内容我们都可以复用成组件。但实际上,我们也会发现过度的组件化会导致代码量上升、开发时间增加以及额外的数据传递等等。所以如果不打算制作一个完整的组件库,那么在实际项目中做到按需拆分、整合即可,不用过分的追求每个可复用的部分都写成单个组件。

为什么需要一个提示组件

因为alert大部分时间不能满足我们的需求啊。往往项目里需要一个类似于alert的东西,用美观、可定制的方式提示用户一些信息,因此这样一个提示组件很有必要。

同时,我们也不希望同一时间出现多个提示混淆用户,因此在设计上,我们将提示组件设定为具有唯一性,整个应用中各个视图调用的都是同一个提示组件。

Show me the code

接下来,由简入繁依次实现提示组件的各个功能。

基本功能

最基本的功能当然是触发后显示,并且能够以某种方式关闭。唯一需要自定义的部分,就是具体显示的内容。所以最开始组件长这样:

<template>
 <div class="notification fixed"
 v-if="show"
 transition="slide">
  <div class="delete"
  @click="close()"></div>
  <div class="content">
   {{ options.content }}
  </div>
 </div>
</template>

<script>
export default {
 props: {
  options: {
   type: Object,
   default: () => {
    return {}
   }
  },
  show: {
   type: Boolean,
   default: false
  }
 },
 methods: {
  close () {
   this.show = false
   this.options = {}
  }
 }
}
</script>

<style scoped lang="sass">
 .slide-transition
  transition: all .3s ease
  transform: translate3d(0, 0, 0)
 .slide-enter,
 .slide-leave
  transform: translate3d(0, -100%, 0)
 .delete
  -moz-appearance: none
  -webkit-appearance: none
  background: rgba(51,51,51,0.2)
  cursor: pointer
  display: inline-block
  height: 24px
  position: relative
  vertical-align: top
  width: 24px
  float: right
  &:before,
  &:after
   background: #fff
   content: ""
   display: block
   height: 2px
   left: 50%
   margin-left: -25%
   margin-top: -1px
   position: absolute
   top: 50%
   width: 50%
  &:before
   transform: rotate(45deg)
  &:after
   transform: rotate(-45deg)
  &:hover
   background: rgba(51,51,51,0.5)
 .notification
  width: 100%
  line-height: 2
  z-index: 3
  position: fixed
  top: 0
  left: 0
  .content
   padding: .75rem 2rem
</style>

思路很简单,props传递两个数据,show用于控制显示,options传入包括内容在内的自定义内容。为了让提示的显示更加自然,添加了一个滑动进入和离开的transition。

注意:这里的关闭按钮是通过css实现的,如果在你的项目中有对应的icon,可以将其替换掉。

在此处,也可以使用slot来进行内容的传递,但考虑到之后还有别的参数需要传递至组件内,一次用一个统一的对象options进行传递。

自定义样式

通常提示的内容种类很多,有的是成功提示,有的是警告,有的则是报错。因此我们需要定义不同的样式以表达不同的内容。方法很简单,在options中传入背景色和文字颜色两个参数,如果组件中检测到了传入的样式参数,就用其替换默认样式。

Vue.js在处理动态样式时非常灵活,为了让代码更清晰,我没有选择将动态样式内联,而是单独使用一个计算属性setStyle进行设定:

computed: {
 setStyle () {
  return {
   color: this.options.textColor || '#fff',
   background: this.options.backgroundColor || '#21e7b6'
  }
 }
}

这样一来,只要在options中一并传入textColor和backgroundColor两个属性,就可以轻松自定义提示样式了。

自动关闭

很多时候,我们希望提示在一定时间之后可以自动关闭,因此组件也需要扩展出一个自动关闭的模式。同样的,在“数据驱动”的思想下,我们应该提供一个数据,用来表明这个提示是否自动关闭。

options中的autoClose属性就是这个作用。同样的,自动关闭的延迟时间显然也要能够自定义,因此还一同添加了showTime这一属性。

自动关闭本身不太复杂,我们只需要使用setTimeout,定义一个计时器即可。

首先是监听提示组件的显示。

在这里,我通过watch监听options的变化来处罚计时器。由于我们已经定义了一个close方法用于关闭计时器,并且在关闭时重置了show和options的值,所以在options变化时,只需要判断options中的autoClose是否为true,就能知道是否需要启动计时器了。这里单独使用一个countdown方法来处理定时器相关的操作。

新增代码如下:

data () {
 return {
  timers: []
 } 
},
methods: {
 countdown () {
  if (this.options.autoClose) {
   const t = setTimeout(() => {
    this.close()
   }, this.options.showTime || 3000)
   this.timers.push(t)
  }
 }
},
watch: {
 options () {
  this.timers.forEach((timer) => {
   window.clearTimeout(timer)
  })
  this.timers = []
  this.countdown()
 }
}

细心地你肯定会发现,这段代码中,有一些奇怪的处理。我们定义了一个空数组timers,并且每次开始一个计时器的时候,就把计时器存入数组中,而每次options变化时,我们也从timers中遍历所有计时器并取消,之后清空timers。

这个做法,主要是为了避免一个计时器还没有结束时,又开始一个新的提示所引发的提示被提前关闭的清空。举个例子,如果没有这样的处理,那么先发出一个自动关闭的提示,在其没自动关闭之前,就再发出一个新的提示。那么第一个提示的定时器依然会错误的关闭新提示。

这样的问题主要是由于我们所有的计时器都是在同一个组件中,本质上都是同一个提示,因此需要清除计时器,避免冲突。许多组件库中类似的功能组件,是采用每一条提示就新生成一个提示组件的方式来实现的。但是那样在多个提示连续出现时,就会出现堆叠在一起,又各自离开的情况。

之前的版本中,我的提示组件也采用了类似的设计方式,但是在最近的一个项目中,需要实现半透明的提示组件,就出现了堆叠后看不清提示文字的现象,才使用了现在新的模式。

进一步扩展

紧接着,我拓展了一个自动关闭模式下的倒计时条功能。思路上没有使用Vue.js的transition系统,而是采用了Css3本身的动画系统。在一个自动关闭的提示被初始化时,为计时条添加一个样式,效果是向X轴负方向移动100%,transition时间则通过计算属性对应设定。具体实现可以参考源代码,这里不多做赘述。

增强灵活性

最后则是让提示组件更灵活。有的时候,我们想展示的可能是可以自定义样式的文本、亦或是一个超链接甚至更多。而Vue.js实现起来不要太简单。我们只需要将组件中用于渲染的{{ options.content }}变为{{{ options.content }}}即可,对于3重花括号的模板,Vue.js会将其中的HTML标签按照正常内容渲染。

如此一来,我们就可以将任何HTML内容放入提示中了。当然一定要注意避免将用户输入的内容渲染到3重花括号的模板中,避免XSS攻击。

结合vuex

很多时候,我们会把提示组件引入到App.vue这个根组件中,但是发出提示的可能是组件树中的任何一个组件。如果不想代码中遍布各种dispatch和broadcast,那么引入vuex来进行管理是个很好的方案。

大致的思路如下:

// store.js
const state = {
 show: false,
 options: {
  autoClose: false,
  content: 'notice content'
 }
}

const mutations = {
 NEW_NOTICE (state, options) {
  state.show = true
  state.options = options
 },
 CLOSE_NOTICE (state) {
  state.show = false
  state.options = {}
 }
}

// actions.js

export const newNotice = ({dispatch}, options) => {
 dispatch('NEW_NOTICE', options)
}
export const closeNotice = ({dispatch}) => {
 dispatch('CLOSE_NOTICE')
}

// Notification.vue

vuex: {
 getters: {
  show: state => state.show
  options: state => state.options
 },
 actions: {
  close: closeNotice
 }
}

// 任意调用notice的组件

vuex: {
 actions: {
  notice: newNotice
 }
}

引入vuex后,按上述代码进行配置,就可以在任意一处组件中,使用this.notice({options})传递数据。不过由于vuex的单项数据流动特性,所有对state数据的操作都必须经过actions调用mutations实现,包括提示组件中的close方法也要替换成actions中的closeNotice方法。

综述

通过这个提示组件,我们更熟练的掌握了Vue.js的组件系统、数据传递、计算属性、transition动画等特性。另外此组件已经可以直接用于生产环境中,欢迎star、fork、pr。

下载地址:vue-notie_3water.rar

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
Js callBack 返回前一页的js方法
Nov 30 Javascript
JQuery操作Select的Options的Bug(IE8兼容性视图模式)
Apr 21 Javascript
浅析XMLHttpRequest的缓存问题
Dec 13 Javascript
jQuery隐藏和显示效果实现
Apr 06 Javascript
微信小程序 网络API 上传、下载详解
Nov 09 Javascript
javascript中call,apply,bind函数用法示例
Dec 19 Javascript
AngularJs每天学习之总体介绍
Aug 07 Javascript
jQuery动态移除与增加onclick属性的方法详解
Jun 07 jQuery
原生js实现的观察者和订阅者模式简单示例
Apr 18 Javascript
鸿蒙系统中的 JS 开发框架
Sep 18 Javascript
JavaScript实现雪花飘落效果
Dec 27 Javascript
JS实现选项卡插件的两种写法(jQuery和class)
Dec 30 jQuery
javascript 动态生成css代码的两种方法
Mar 17 #Javascript
AngularJS中$http使用的简单介绍
Mar 17 #Javascript
单击按钮发送验证码,出现倒计时的简单实例
Mar 17 #Javascript
jQuery Ajax前后端使用JSON进行交互示例
Mar 17 #Javascript
Bootstrap表格制作代码
Mar 17 #Javascript
鼠标经过出现气泡框的简单实例
Mar 17 #Javascript
angularJS之$http:与服务器交互示例
Mar 17 #Javascript
You might like
PHP实现使用优酷土豆视频地址获取swf播放器分享地址
2014/06/05 PHP
phpMyAdmin无法登陆的解决方法
2017/04/27 PHP
PHP SESSION机制的理解与实例
2019/03/22 PHP
DWZ刷新dialog解决方法
2013/03/03 Javascript
jQuery判断iframe中元素是否存在的方法
2013/05/11 Javascript
从数据库读取数据后将其输出成html标签的三种方法
2014/10/13 Javascript
jQuery validate插件实现ajax验证重复的2种方法
2016/01/22 Javascript
vue实现列表的添加点击
2016/12/29 Javascript
详解Vue2.0里过滤器容易踩到的坑
2017/06/01 Javascript
jquery实现放大镜简洁代码(推荐)
2017/06/08 jQuery
JavaScript实现跟随滚动缓冲运动广告框
2017/07/15 Javascript
React Native验证码倒计时工具类分享
2017/10/24 Javascript
Vue.js 时间转换代码及时间戳转时间字符串
2018/10/16 Javascript
微信小程序添加插屏广告并设置显示频率(一天一次)
2019/12/06 Javascript
解决vue+ element ui 表单验证有值但验证失败问题
2020/01/16 Javascript
js实现树形数据转成扁平数据的方法示例
2020/02/27 Javascript
[54:28]EG vs OG 2019国际邀请赛小组赛 BO2 第一场 8.16
2019/08/18 DOTA
wxpython 学习笔记 第一天
2009/02/09 Python
python对url格式解析的方法
2015/05/13 Python
python实现基本进制转换的方法
2015/07/11 Python
微信跳一跳小游戏python脚本
2018/01/05 Python
python实现三次样条插值
2018/12/17 Python
Python爬虫图片懒加载技术 selenium和PhantomJS解析
2019/09/18 Python
python基于TCP实现的文件下载器功能案例
2019/12/10 Python
Tensorflow 多线程与多进程数据加载实例
2020/02/05 Python
K最近邻算法(KNN)---sklearn+python实现方式
2020/02/24 Python
CSS3悬停效果案例应用
2012/11/21 HTML / CSS
HTML中meta标签及Keywords
2020/04/15 HTML / CSS
英国最大的手表网站:The Watch Hut
2017/03/31 全球购物
高级人员简历的自我评价分享
2013/11/03 职场文书
优秀教师感人事迹材料
2014/05/04 职场文书
十佳中学生事迹材料
2014/06/02 职场文书
2014年大学生党员评议表自我评价
2014/09/20 职场文书
先进个人总结范文
2015/02/15 职场文书
MySQL中EXPLAIN语句及用法
2022/05/20 MySQL
如何解决flex文本溢出问题小结
2022/07/15 HTML / CSS