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

这次的教程里,我们要把组件化进行到底!最近半年的几个项目中,都遇到了需要使用Toast或者Notification组件的情况。在目前已有的一些基于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_jb51.rar

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

(0)

相关推荐

  • 详解Vue.js组件可复用性的混合(mixin)方式和自定义指令

    混合是什么 混合 (mixins) 是一种分发 Vue 组件中可复用功能的非常灵活的方式.混合对象可以包含任意组件选项.以组件使用混合对象时,所有混合对象的选项将被混入该组件本身的选项. 例如: var tpl1={ template:'#stpl1', data:function(){ return {msg:false} }, methods:{ msgf:function(){ this.msg=!this.msg } } } var tpl2={ template:'#stpl2', d

  • 详解打造 Vue.js 可复用组件

    Vue.js 是一套构建用户界面的渐进式框架.我们可以使用简单的 API 来实现响应式的数据绑定和组合的视图组件. 从维护视图到维护数据,Vue.js 让我们快速地开发应用.但随着业务代码日益庞大,组件也越来越多,组件逻辑耦合严重,使代码维护变得十分困难. 同时,Vue.js 的接口和语法十分自由,实现同一功能有若干种方法.每个人解决问题的思路不一样,写出来的代码也就不一样,缺乏团队内的规范. 本文旨在从组件开发的不同方面列举出合理的解决方法,作为建立组件规范的一个参考. 构成组件 组件,是一个

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

    这次的教程里,我们要把组件化进行到底!最近半年的几个项目中,都遇到了需要使用Toast或者Notification组件的情况.在目前已有的一些基于Vue.js开发的组件库,都没有找到太合适的,所以自己重头实现了一个.历经几个项目的磨练,这个提示组件的功能已经越来越完善,这次就分享一下组件以及其实现思路吧. 深入组件化,组件的拆分.整合与复用 Vue.js的组件化可以说是其招牌特性之一,而在实际应用时,并非一味地追求组件颗粒越小越好,而是需要根据项目的实际需求,来分析自己需要什么级别的组件. 例如

  • 一步步教你用Vue.js创建一个组件(附代码示例)

    目录 前言 到底什么是组件? 为什么你一定要使用组件 在Vue中创建一个组件 模板部分 脚本部分 选项API:旧的方式 合成API:现在和未来 风格部分 总结 前言 Vue.js是一个渐进式框架,旨在以一种非常简单.直接的方式构建用户界面.它被设计成易于使用,并且足够灵活,可以处理各种各样的应用. 在本教程中,我们将向你展示如何用Vue.js创建一个简单的组件.我们还将介绍一些在使用组件时需要知道的基本概念. 我们将介绍在Vue中创建一个组件的基本语法,以及一些关于组件用途的理论.在这篇文章的最

  • 使用Vue.js创建一个时间跟踪的单页应用

    Vue.js很简单.正因为如此简单,人们常常认为其适合于小项目.虽然真正的Vue.js核心知识只是一个视图层库,实际上有一组工具,将使您能够使用Vue.js构建完整的大规模SPA(单页应用程序). SPA应用可以在不完全重新加载网页,产生一个更流畅的用户体验到的用户交互响应.还有好的副作用,SPA还鼓励后端专注于展示数据端点,这使得整体架构更加分离,并且对于其他类型的客户端可能是可重用的. 从开发人员的角度来看,SPA和传统的后端呈现应用程序之间的主要区别是,我们必须将客户端视为具有自己架构的应

  • Vue.js实现一个todo-list的上移下移删除功能

    如图,A simple todo-list长这样 这是一个基于vue.js的一个简单的todo-list小demo.首先要实现添加非空list,点击list切换finished状态这样的一个效果,推荐学习地址---->点击打开链接 接下来是实现的一个上移,下移,删除的效果图: 删除效果: 讲一下思路: 上移-----首先将鼠标所指list的内容插入到上一条上面,然后删除鼠标所指的list(也就是this.items[index]),运行代码便可实现上移的效果,或者将上下两条list的内容进行调换

  • vue.js做一个简单的编辑菜谱功能

    先给大家展示下效果图,如果感觉不错,请参考实现代码 1.先获取门店下的所有菜品类型.菜品名称.菜品id(list),也就是最大数据量 this.$http.post(ceshiApi+'getCyFoodAndFoodTypeForShopId',{shopId:this.shopId},{emulateJSON:true,credentials: true}).then(function(res){ if(res.data.type=='success'){ this.foodList = r

  • 利用Vue.js制作一个拼图华容道小游戏

    目录 游戏介绍 核心思路 核心代码 html games 类 生成随机图片数量 移动图片 键盘事件 拼图完成 结语 游戏介绍 先看看界面 这是一个拼图游戏,可以自选难度和自选闯关图片 游戏开始后根据不同难度,生成与所选主图 对应的 不同张数的 随机顺序的小图,然后只要把乱序的小图片还原成完整的图片就闯关成功 游戏区域有一个空白位置,可以用鼠标点击空白位相邻的图片完成替换,也就是移动,也可以用键盘上下左右操作 游戏好玩,可不要贪杯哦,学习也不能落下,不管什么游戏都一样 这个虽然用到的技术很一般很简

  • vue.js基于ElementUI封装了CRUD的弹框组件

    目录 前言 开始封装 json对象如下所示 table表头作为列表传入,数据结构如下 在子组件中循环渲染出表头 在父组件中调用 前言 代码写得不好,为什么不封装一下呢,如果用的是ElementUI框架,也可以在此基础上进行二次封装.譬如说,这个用来对列表数据进行增删改查的弹框. 开始封装 原本只是个小功能,但是别的模块也需要用到. 我的想法就是,把弹框标题,table表头,必填字节,接口请求路径,增删改查CRUD,等等,放在一个json对象里面.通过父组件向子组件传参的方式,展示不同内容,调用不

  • Vue.js实现一个自定义分页组件vue-paginaiton

    vue实现一个分页组件vue-paginaiton vue使用了一段时间的感触就是,我再也不想直接操作DOM了.数据绑定式的编程体验真是好.实现的一个分页组件. 这里的css就不放出来了,可以看直接去github上下载:vue-pagination 先上一张实例图吧 模版 <div class="page-bar"> <ul> <li v-if="showFirstText"><a v-on:click="cur-

  • Vue.js实现一个SPA登录页面的过程【推荐】

    技术栈 vue.js 主框架 vuex 状态管理 vue-router 路由管理 一般过程 在一般的登录过程中,一种前端方案是: 检查状态:进入页面时或者路由变化时检查是否有登录状态(保存在cookie或者本地存储的值): 如果有登录态则查询登录信息(uid,头像等...)并保存起来:如果没有则跳转到登录页: 在登录页面(或者登录框),校检用户输入信息是否合法: 校检通过后发送登录请求:校检不成功则反馈给用户: 登录成功则从后端数据中取出session信息保存登录状态(可能需要跳转);登录不成功

  • 使用vue.js写一个tab选项卡效果

    通常我们写tab选项卡的时候,一般都是用jq等去操作dom,给同级元素移除active类,然后,给被点击元素添加active类,但是在vue.js中,我们能不去操作dom我们就尽量不操作dom,那么该如何实现呢? 如果使用过vue-router,那么你会发现,vue-router在使用的时候其实就相当于一个tab选项卡,在点击之后,被点击的router-link元素会默认被添加上一个router-link-active的类,我们只需要设置这个类的样式即可.(当然,router-link-acti

随机推荐