你不知道的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 相关文章推荐
让textarea自动调整大小的js代码
Apr 12 Javascript
防止按钮在短时间内被多次点击的方法
Mar 10 Javascript
jQuery Ajax调用WCF服务详细教程
Mar 31 Javascript
JS动态创建DOM元素的方法
Jun 09 Javascript
jQuery实现的鼠标滑过弹出放大图片特效
Jan 08 Javascript
jQuery实现指定区域外单击关闭指定层的方法【经典】
Jun 22 Javascript
解决Angular.Js与Django标签冲突的方案
Dec 20 Javascript
JavaScript实现二叉树的先序、中序及后序遍历方法详解
Oct 26 Javascript
jQuery封装animate.css的实例
Jan 04 jQuery
zepto.js 实时监听输入框的方法
Dec 04 Javascript
layui-table获得当前行的上/下一行数据的例子
Sep 24 Javascript
小程序选项卡以及swiper套用(跨页面)
Jun 19 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
PHP 基本语法格式
2009/12/15 PHP
php数组中删除元素之重新索引的方法
2014/09/16 PHP
php静态文件生成类实例分析
2015/01/03 PHP
Zend Framework实现多文件上传功能实例
2016/03/21 PHP
自定义min版smarty模板引擎MinSmarty.class.php文件及用法
2016/05/20 PHP
php实现对文件压缩简单的方法
2019/09/29 PHP
php+laravel依赖注入知识点总结
2019/11/04 PHP
破解Session cookie的方法
2006/07/28 Javascript
避免回车键导致的页面无意义刷新的解决方法
2011/04/12 Javascript
Extjs实现进度条的两种便捷方式
2013/09/26 Javascript
JS+DIV实现鼠标划过切换层效果的实例代码
2013/11/26 Javascript
connect中间件session、cookie的使用方法分享
2014/06/17 Javascript
前端必备神器 Snap.svg 弹动效果
2014/11/10 Javascript
javascript 中__proto__和prototype详解
2014/11/25 Javascript
JS实现表单中checkbox对勾选中增加边框显示效果
2015/08/21 Javascript
JavaScript中Math对象的方法介绍
2017/01/05 Javascript
深入了解JavaScript 私有化
2019/05/30 Javascript
微信小程序 自定义复选框实现代码实例
2019/09/04 Javascript
详解Typescript 内置的模块导入兼容方式
2020/05/31 Javascript
Python  连接字符串(join %)
2008/09/06 Python
利用python求相邻数的方法示例
2017/08/18 Python
PyCharm 2020.1版安装破解注册码永久激活(激活到2089年)
2020/09/24 Python
Python在centos7.6上安装python3.9的详细教程(默认python版本为2.7.5)
2020/10/15 Python
通过实例解析python and和or使用方法
2020/11/14 Python
python生成word合同的实例方法
2021/01/12 Python
详解HTML5中的拖放事件(Drag 和 drop)
2016/11/14 HTML / CSS
英国最大的百货公司:Harrods
2016/08/18 全球购物
美国著名的户外用品品牌:L.L.Bean
2018/01/05 全球购物
加拿大在线眼镜零售商:SmartBuyGlasses加拿大
2019/05/25 全球购物
老教师工作总结的自我评价
2013/09/27 职场文书
新郎婚宴答谢词
2014/01/19 职场文书
网络编辑职责
2014/03/01 职场文书
个人委托书
2014/07/31 职场文书
党员群众路线自我剖析材料
2014/10/06 职场文书
2015羊年春节慰问信
2015/02/14 职场文书
MySQL分区以及建索引的方法总结
2022/04/13 MySQL