你不知道的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 面向对象的技术创建高级 Web 应用程序
Feb 25 Javascript
jquery获取被勾选的checked(选中)的那一行的3列和4列的值
Jul 04 Javascript
JavaScript显示表单内元素数量的方法
Apr 02 Javascript
JavaScript数据存储 Cookie篇
Jul 02 Javascript
canvas实现探照灯效果
Feb 07 Javascript
vue2.0获取自定义属性的值
Mar 28 Javascript
ReactJS实现表单的单选多选和反选的示例
Oct 13 Javascript
Angular之toDoList的实现代码示例
Dec 02 Javascript
浅谈VUE监听窗口变化事件的问题
Feb 24 Javascript
vue中 this.$set的用法详解
Sep 06 Javascript
Vuex的热更替如何实现
Jun 05 Javascript
vue 实现动态路由的方法
Jul 06 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
我的论坛源代码(五)
2006/10/09 PHP
php计算程序运行时间的简单例子分享
2014/05/10 PHP
PHP改进计算字符串相似度的函数similar_text()、levenshtein()
2014/10/27 PHP
9个实用的PHP代码片段分享
2015/01/22 PHP
php获取本周星期一具体日期的方法
2015/04/20 PHP
深入剖析PHP中printf()函数格式化使用
2016/05/23 PHP
php使用curl实现简单模拟提交表单功能
2017/05/15 PHP
Javascript代码混淆综合解决方案-Javascript在线混淆器
2006/12/18 Javascript
javascript options属性集合操作代码
2009/12/28 Javascript
JQuery UI DatePicker中z-index默认为1的解决办法
2010/09/28 Javascript
Nodejs学习笔记之NET模块
2015/01/13 NodeJs
javascript异步编程代码书写规范Promise学习笔记
2015/02/11 Javascript
基于原生JS实现图片裁剪
2016/08/01 Javascript
关于Sequelize连接查询时inlude中model和association的区别详解
2017/02/27 Javascript
详解打造 Vue.js 可复用组件
2017/03/24 Javascript
javascript简单链式调用案例分析
2017/05/10 Javascript
详解用node编写自己的cli工具
2017/05/23 Javascript
jQuery简单绑定单个事件的方法示例
2017/06/10 jQuery
为什么我们要做三份 Webpack 配置文件
2017/09/18 Javascript
node.js学习之断言assert的使用示例
2017/09/28 Javascript
js实现购物车功能
2018/06/12 Javascript
Babel 入门教程学习笔记
2018/06/13 Javascript
Array数组对象中的forEach、map、filter及reduce详析
2018/08/02 Javascript
详解js中Array的方法及技巧
2018/09/12 Javascript
python 递归遍历文件夹,并打印满足条件的文件路径实例
2017/08/30 Python
对numpy下的轴交换transpose和swapaxes的示例解读
2019/06/26 Python
Python企业编码生成系统之主程序模块设计详解
2019/07/26 Python
pandas的resample重采样的使用
2020/04/24 Python
基于MUI框架使用HTML5实现的二维码扫描功能
2018/03/01 HTML / CSS
护理专业毕业生推荐信
2013/10/31 职场文书
安全负责人任命书
2014/06/06 职场文书
公司年终奖分配方案
2014/06/16 职场文书
学生个人评语大全
2015/01/04 职场文书
推普标语口号大全
2015/12/26 职场文书
股权投资协议书
2016/03/23 职场文书
Vue如何实现组件间通信
2021/05/15 Vue.js