你不知道的Vue技巧之--开发一个可以通过方法调用的组件(推荐)


Posted in Javascript onApril 15, 2019

Vue作为最近最炙手可热的前端框架,其简单的入门方式和功能强大的API是其优点。而同时因为其API的多样性和丰富性,所以他的很多开发方式就和一切基于组件的React不同,如果没有对Vue的API(有一些甚至文档都没提到)有一个全面的了解,那么在开发和设计一个组件的时候有可能就会绕一个大圈子,所以我非常推荐各位在学习Vue的时候先要对Vue核心的所有API都有一个了解。

举个例子,通知组件notification基本是现代web开发标配,在很多地方都能用到。而在以Vue作为核心框架的前端项目中,因为Vue本身是一个组件化和虚拟Dom的框架,要实现一个通知组件的展示当然是非常简单的。但因为通知组件的使用特性,直接在模板当中书写组件并通过v-show或者props控制通知组件的显示显然是非常不方便的,而且如果要在action或者其他非组件场景中要用到通知,那么纯组件模式的用法也无法实现。那么有没有办法即用到Vue组件化特性方便得实现一个通知组件的展现,又能够通过一个简单的方法调用就能显示通知呢?本文就是来讲述这个实现方法的。

目标

实现一个Vue的通知组件,可以直接在组件内调用
通过方法调用,比如Vue.$notify({...options})来调用通知组件
结合上述两种方式,复用代码

实现通知组件

这一步非常的简单,我相信做过一点Vue开发的同学都能写出一个像模像样的通知组件,在这里就不赘述,直接上代码

<template>
 <transition name="fade" @after-leave="afterLeave" @after-enter="setHeight">
  <div
   v-show="visible"
   :class="['notification']"
   :style="style"
   @mouseenter="clearTimer"
   @mouseleave="createTimer"
  >
   <span class="content">{{content}}</span>
   <a class="btn" @click="handleClose">{{btn || '关闭'}}</a>
  </div>
 </transition>
</template>

<script>
export default {
 name: 'Notification',
 props: {
  content: {
   type: String,
   default: ''
  },
  btn: {
   type: String,
   default: ''
  }
 },
 data () {
  return {
   visible: true
  }
 },
 computed: {
  style () {
   return {}
  }
 },
 methods: {
  handleClose (e) {
   e.preventDefault()
   this.doClose()
  },
  doClose () {
   this.visible = false
   this.$emit('close')
  },
  afterLeave () {
   this.$emit('closed')
  },
  clearTimer () {},
  createTimer () {},
  setHeight () {}
 }
}
</script>
<style lang="stylus" scoped>
.notification
 display: flex
 background-color #303030
 color rgba(255, 255, 255, 1)
 align-items center
 padding 20px
 position fixed
 min-width 280px
 box-shadow 0px 3px 5px -1px rgba(0, 0, 0, 0.2), 0px 6px 10px 0px rgba(0, 0, 0, 0.14), 0px 1px 18px 0px rgba(0, 0, 0, 0.12)
 flex-wrap wrap
 transition all .3s
.content
 padding 0
.btn
 color #ff4081
 padding-left 24px
 margin-left auto
 cursor pointer
</style>

在这里需要注意,我们定义了一个叫做style的computed属性,三个方法clearTimer,createTimer,setHeight,但他们的内容都是空的,虽然在模板上有用到,但是似乎没什么意义,在后面我们要扩展组件的时候我会讲到为什么要这么做。

创建完这个组件之后,我们就可以在模板中使用了<notification btn="xxx" content="xxx" />

实现通过方法调用该通知组件

继承组件

在实现通过方法调用之前,我们需要扩展一下这个组件,因为仅仅这些属性,并不够我们使用。在使用方法调用的时候,我们需要考虑一下几个问题:

  1. 显示通知的定位
  2. 组件的出现和自动消失控制
  3. 连续多次调用通知方法,如何排版多个通知

在这个前提下,我们需要扩展该组件,但是扩展的这些属性不能直接放在原组件内,因为这些可能会影响组件在模板内的使用,那怎么办呢?这时候我们就要用到Vue里面非常好用的一个API,extend,通过他去继承原组件的属性并扩展他。

我们先来看代码,创建一个叫做fun-notification.js的文件,内容如下:

import Notification from './notification.vue'

export default {
 extends: Notification,
 computed: {
  style () {
   return {
    position: 'fixed',
    right: '20px',
    bottom: `${this.verticalOffset + 20}px`
   }
  }
 },
 data () {
  return {
   verticalOffset: 0,
   visible: false,
   height: 0,
   autoClose: 3000
  }
 },
 mounted () {
  this.createTimer()
 },
 methods: {
  createTimer () {
   if (this.autoClose) {
    this.timer = setTimeout(() => {
     this.doClose()
    }, this.autoClose)
   }
  },
  clearTimer () {
   if (this.timer) {
    clearTimeout(this.timer)
   }
  },
  setHeight () {
   this.height = this.$el.offsetHeight
  }
 }
}

我们可以看到之前空实现的几个方法在这里被实现了,那么为什么要在原组件上面加上那些方法的定义呢?因为需要在模板上绑定,而模板是无法extend的,只能覆盖,如果要覆盖重新实现,那扩展的意义就不是很大了。当然同学们可以自己抉择。

在使用extend的时候注意以下两个点:

  1. 方法和属性的定义是直接覆盖的
  2. 生命周期方法类似余mixin,会合并,也就是原组件和继承之后的组件都会被调用,原组件先调用

通过方法调用该组件

最后我们需要做的就是通过方法调用这个已经继承过的组件了,我们先来看一下源码的实现:

// function-component.js
import Vue from 'vue'
import Component from './fun-component'
const NotificationConstructor = Vue.extend(Component)

const instances = []
let seed = 1

const removeInstance = (instance) => {
 const len = instances.length
 if (!instance) return
 const index = instances.findIndex(inst => instance.id === inst.id)

 instances.splice(index, 1)

 if (len <= 1) return
 const removedHeight = instance.vm.height
 for (let i = index; i < len - 1; i++) {
  instances[i].verticalOffset =
   parseInt(instances[i].verticalOffset) - removedHeight - 16
 }
}

const notify = function (options) {
 const {
  onClose,
  ...rest
 } = options
 if (Vue.prototype.$isServer) return
 options = options || {}
 const id = `notification_${seed++}`

 const instance = new NotificationConstructor({
  propsData: {
   ...rest
  }
 })

 instance.id = id
 instance.vm = instance.$mount()
 document.body.appendChild(instance.vm.$el)
 instance.vm.visible = true

 let verticalOffset = 0
 instances.forEach(item => {
  verticalOffset += item.$el.offsetHeight + 16
 })
 verticalOffset += 16
 instance.verticalOffset = verticalOffset
 instances.push(instance)
 instance.vm.$on('closed', () => {
  if (typeof onClose === 'function') {
   onClose(instance)
  }
  removeInstance(instance)
  instance.vm.$destroy()
 })
 return instance.vm
}

export default notify

首先通过const NotificationConstructor = Vue.extend(Component),我们得到了一个类似于Vue的子类,我们就可以通过new NotificationConstructor({...options})的方式去创建Vue的实例了,同时通过该方式创建的实例,是有组件定义里面的所有属性的。

在创建实例之后,可以通过instance.$mount()手动将组件挂载到DOM上面,这样我们可以不依赖Vue组件树来输出DOM片段,达到自由显示通知的效果。

这中间的实现主要就是维护一个通知数组,在创建时推入,在消失时删除,这个过程并没有规定一定要如此实现,我就不赘述,以免限制大家的思路,大家可以根据自己的想法去实现。

使用该方法

要使用这个通知方法非常简单,我们可以直接import这个文件来使用,比如:

import notify from './function-component.js'

notify({
 content: 'xxx',
 btn: 'xxx'
})

当然我们很多场景是在组件内部调用,为了方便在组件内使用,不需要每次都import,我们可以把这个方法包装成一个Vue的插件。我们创建一个index.js,内容如下:

import Notification from './notification.vue'
import notify from './function'

export default (Vue) => {
 Vue.component(Notification.name, Notification)
 Vue.prototype.$notify = notify
 Vue.notify = notify
}

然后在项目内,我们可以通过:

import notify from '/path/to/notification/module'

Vue.use(notify)

这样之后,在组件内就可以直接通过this.$notify({...options})来调用通知了,同时还可以通过Vue.notify({...options})在其他环境下调用,大家可以在自己的项目中尝试一下。

总结

到这里,关于如何实现通过方法调用一个Vue组件内容就差不多了。在这里我们涉及到的Vue技术点有如下几点:

  1. 通过extend配置进行组件的扩展
  2. 通过Vue.extend创建一个Vue的子类,用来动态创建Vue实例
  3. 通过Vue实例主动将组件内容挂载到DOM

Vue拥有非常多的API,如果在使用Vue之前没有系统的学习过Vue的核心知识和API,你可能压根就不知道有这样的实现方式,所以想要学好Vue,系统得对Vue的核心进行学习是非常重要的一个环节。

以上所述是小编给大家介绍的你不知道的Vue技巧之--开发一个可以通过方法调用的组件详解整合,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

Javascript 相关文章推荐
js实现简单登录功能的实例代码
Nov 09 Javascript
JS 日期比较大小的简单实例
Jan 13 Javascript
兼容主流浏览器的JS复制内容到剪贴板
Dec 12 Javascript
jquery实现很酷的网页顶部图标下拉菜单效果
Aug 22 Javascript
JavaScript版经典游戏之扫雷游戏完整示例【附demo源码下载】
Dec 12 Javascript
JS按钮闪烁功能的实现代码
Jul 21 Javascript
使用Vuex实现一个笔记应用的方法
Mar 13 Javascript
利用Vue构造器创建Form组件的通用解决方法
Dec 03 Javascript
electron-vue利用webpack打包实现多页面的入口文件问题
May 12 Javascript
angular4应用中输入的最小值和最大值的方法
May 17 Javascript
js实现双人五子棋小游戏
May 28 Javascript
Vue.js中使用Vuex实现组件数据共享案例
Jul 31 Javascript
详解JavaScript中的强制类型转换
Apr 15 #Javascript
一个小时快速搭建微信小程序的方法步骤
Apr 15 #Javascript
详解从0开始搭建微信小程序(前后端)的全过程
Apr 15 #Javascript
ES6知识点整理之模块化的应用详解
Apr 15 #Javascript
详解如何运行vue项目
Apr 15 #Javascript
vue单页面在微信下只能分享落地页的解决方案
Apr 15 #Javascript
vue--vuex详解
Apr 15 #Javascript
You might like
追忆往昔!浅谈收音机的百年发展历史
2021/03/01 无线电
php str_pad 函数使用详解
2009/01/13 PHP
php模板中出现空行解决方法
2011/03/08 PHP
深入解析PHP垃圾回收机制对内存泄露的处理
2013/06/14 PHP
Yii获取当前url和域名的方法
2015/06/08 PHP
PHP7.1新功能之Nullable Type用法分析
2016/09/26 PHP
php检测mysql表是否存在的方法小结
2017/07/20 PHP
PHP工厂模式简单实现方法示例
2018/05/23 PHP
用JavaScript实现仿Windows关机效果
2007/03/10 Javascript
JS隐藏参数post传值实例
2013/04/18 Javascript
js由下向上不断上升冒气泡效果实例
2015/05/07 Javascript
JavaScript实现移动端滑动选择日期功能
2016/06/21 Javascript
JavaScript阻止表单提交方法(附代码)
2017/08/15 Javascript
基于angular-utils-ui-breadcrumbs使用心得(分享)
2017/11/03 Javascript
Phaser.js实现简单的跑酷游戏附源码下载
2018/10/26 Javascript
el-table树形表格表单验证(列表生成序号)
2020/05/31 Javascript
解决Mint-ui 框架Popup和Datetime Picker组件滚动穿透的问题
2020/11/04 Javascript
详解vue修改elementUI的分页组件视图没更新问题
2020/11/13 Javascript
http请求 request失败自动重新尝试代码示例
2018/01/25 Python
python对于requests的封装方法详解
2019/01/03 Python
开启Django博客的RSS功能的实现方法
2020/02/17 Python
Anconda环境下Vscode安装Python的方法详解
2020/03/29 Python
基于python3.7利用Motor来异步读写Mongodb提高效率(推荐)
2020/04/29 Python
tensorflow 2.1.0 安装与实战教程(CASIA FACE v5)
2020/06/30 Python
深入理解HTML的FormData对象
2016/05/17 HTML / CSS
html5是什么_动力节点Java学院整理
2017/07/07 HTML / CSS
使用数据结构给女朋友写个Html5走迷宫游戏
2019/11/26 HTML / CSS
机关党员2014全国两会学习心得体会
2014/03/10 职场文书
网站客服岗位职责
2014/04/05 职场文书
学校四群教育实施方案
2014/06/12 职场文书
六一亲子活动感想
2015/08/07 职场文书
阳光体育运动标语口号
2015/12/26 职场文书
社交电商模式的兴起:这些新的商机千万别错过
2019/07/26 职场文书
浅谈mysql执行过程以及顺序
2021/05/12 MySQL
Windows 11上手初体验:任务栏和开始菜单等迎来大改
2021/11/21 数码科技
Java+swing实现抖音上的表白程序详解
2022/06/25 Java/Android