你不知道的Vue技巧之--開(kāi)發(fā)一個(gè)可以通過(guò)方法調(diào)用的組件(推薦)
Vue作為最近最炙手可熱的前端框架,其簡(jiǎn)單的入門方式和功能強(qiáng)大的API是其優(yōu)點(diǎn)。而同時(shí)因?yàn)槠銩PI的多樣性和豐富性,所以他的很多開(kāi)發(fā)方式就和一切基于組件的React不同,如果沒(méi)有對(duì)Vue的API(有一些甚至文檔都沒(méi)提到)有一個(gè)全面的了解,那么在開(kāi)發(fā)和設(shè)計(jì)一個(gè)組件的時(shí)候有可能就會(huì)繞一個(gè)大圈子,所以我非常推薦各位在學(xué)習(xí)Vue的時(shí)候先要對(duì)Vue核心的所有API都有一個(gè)了解。
舉個(gè)例子,通知組件notification基本是現(xiàn)代web開(kāi)發(fā)標(biāo)配,在很多地方都能用到。而在以Vue作為核心框架的前端項(xiàng)目中,因?yàn)閂ue本身是一個(gè)組件化和虛擬Dom的框架,要實(shí)現(xiàn)一個(gè)通知組件的展示當(dāng)然是非常簡(jiǎn)單的。但因?yàn)橥ㄖM件的使用特性,直接在模板當(dāng)中書(shū)寫(xiě)組件并通過(guò)v-show或者props控制通知組件的顯示顯然是非常不方便的,而且如果要在action或者其他非組件場(chǎng)景中要用到通知,那么純組件模式的用法也無(wú)法實(shí)現(xiàn)。那么有沒(méi)有辦法即用到Vue組件化特性方便得實(shí)現(xiàn)一個(gè)通知組件的展現(xiàn),又能夠通過(guò)一個(gè)簡(jiǎn)單的方法調(diào)用就能顯示通知呢?本文就是來(lái)講述這個(gè)實(shí)現(xiàn)方法的。
目標(biāo)
實(shí)現(xiàn)一個(gè)Vue的通知組件,可以直接在組件內(nèi)調(diào)用
通過(guò)方法調(diào)用,比如Vue.$notify({...options})來(lái)調(diào)用通知組件
結(jié)合上述兩種方式,復(fù)用代碼
實(shí)現(xiàn)通知組件
這一步非常的簡(jiǎn)單,我相信做過(guò)一點(diǎn)Vue開(kāi)發(fā)的同學(xué)都能寫(xiě)出一個(gè)像模像樣的通知組件,在這里就不贅述,直接上代碼
<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 || '關(guān)閉'}}</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>
在這里需要注意,我們定義了一個(gè)叫做style的computed屬性,三個(gè)方法clearTimer,createTimer,setHeight,但他們的內(nèi)容都是空的,雖然在模板上有用到,但是似乎沒(méi)什么意義,在后面我們要擴(kuò)展組件的時(shí)候我會(huì)講到為什么要這么做。
創(chuàng)建完這個(gè)組件之后,我們就可以在模板中使用了<notification btn="xxx" content="xxx" />
實(shí)現(xiàn)通過(guò)方法調(diào)用該通知組件
繼承組件
在實(shí)現(xiàn)通過(guò)方法調(diào)用之前,我們需要擴(kuò)展一下這個(gè)組件,因?yàn)閮H僅這些屬性,并不夠我們使用。在使用方法調(diào)用的時(shí)候,我們需要考慮一下幾個(gè)問(wèn)題:
- 顯示通知的定位
- 組件的出現(xiàn)和自動(dòng)消失控制
- 連續(xù)多次調(diào)用通知方法,如何排版多個(gè)通知
在這個(gè)前提下,我們需要擴(kuò)展該組件,但是擴(kuò)展的這些屬性不能直接放在原組件內(nèi),因?yàn)檫@些可能會(huì)影響組件在模板內(nèi)的使用,那怎么辦呢?這時(shí)候我們就要用到Vue里面非常好用的一個(gè)API,extend,通過(guò)他去繼承原組件的屬性并擴(kuò)展他。
我們先來(lái)看代碼,創(chuàng)建一個(gè)叫做fun-notification.js的文件,內(nèi)容如下:
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 } } }
我們可以看到之前空實(shí)現(xiàn)的幾個(gè)方法在這里被實(shí)現(xiàn)了,那么為什么要在原組件上面加上那些方法的定義呢?因?yàn)樾枰谀0迳辖壎?,而模板是無(wú)法extend的,只能覆蓋,如果要覆蓋重新實(shí)現(xiàn),那擴(kuò)展的意義就不是很大了。當(dāng)然同學(xué)們可以自己抉擇。
在使用extend的時(shí)候注意以下兩個(gè)點(diǎn):
- 方法和屬性的定義是直接覆蓋的
- 生命周期方法類似余mixin,會(huì)合并,也就是原組件和繼承之后的組件都會(huì)被調(diào)用,原組件先調(diào)用
通過(guò)方法調(diào)用該組件
最后我們需要做的就是通過(guò)方法調(diào)用這個(gè)已經(jīng)繼承過(guò)的組件了,我們先來(lái)看一下源碼的實(shí)現(xiàn):
// 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
首先通過(guò)const NotificationConstructor = Vue.extend(Component),我們得到了一個(gè)類似于Vue的子類,我們就可以通過(guò)new NotificationConstructor({...options})的方式去創(chuàng)建Vue的實(shí)例了,同時(shí)通過(guò)該方式創(chuàng)建的實(shí)例,是有組件定義里面的所有屬性的。
在創(chuàng)建實(shí)例之后,可以通過(guò)instance.$mount()手動(dòng)將組件掛載到DOM上面,這樣我們可以不依賴Vue組件樹(shù)來(lái)輸出DOM片段,達(dá)到自由顯示通知的效果。
這中間的實(shí)現(xiàn)主要就是維護(hù)一個(gè)通知數(shù)組,在創(chuàng)建時(shí)推入,在消失時(shí)刪除,這個(gè)過(guò)程并沒(méi)有規(guī)定一定要如此實(shí)現(xiàn),我就不贅述,以免限制大家的思路,大家可以根據(jù)自己的想法去實(shí)現(xiàn)。
使用該方法
要使用這個(gè)通知方法非常簡(jiǎn)單,我們可以直接import這個(gè)文件來(lái)使用,比如:
import notify from './function-component.js' notify({ content: 'xxx', btn: 'xxx' })
當(dāng)然我們很多場(chǎng)景是在組件內(nèi)部調(diào)用,為了方便在組件內(nèi)使用,不需要每次都import,我們可以把這個(gè)方法包裝成一個(gè)Vue的插件。我們創(chuàng)建一個(gè)index.js,內(nèi)容如下:
import Notification from './notification.vue' import notify from './function' export default (Vue) => { Vue.component(Notification.name, Notification) Vue.prototype.$notify = notify Vue.notify = notify }
然后在項(xiàng)目?jī)?nèi),我們可以通過(guò):
import notify from '/path/to/notification/module' Vue.use(notify)
這樣之后,在組件內(nèi)就可以直接通過(guò)this.$notify({...options})來(lái)調(diào)用通知了,同時(shí)還可以通過(guò)Vue.notify({...options})在其他環(huán)境下調(diào)用,大家可以在自己的項(xiàng)目中嘗試一下。
總結(jié)
到這里,關(guān)于如何實(shí)現(xiàn)通過(guò)方法調(diào)用一個(gè)Vue組件內(nèi)容就差不多了。在這里我們涉及到的Vue技術(shù)點(diǎn)有如下幾點(diǎn):
- 通過(guò)extend配置進(jìn)行組件的擴(kuò)展
- 通過(guò)Vue.extend創(chuàng)建一個(gè)Vue的子類,用來(lái)動(dòng)態(tài)創(chuàng)建Vue實(shí)例
- 通過(guò)Vue實(shí)例主動(dòng)將組件內(nèi)容掛載到DOM
Vue擁有非常多的API,如果在使用Vue之前沒(méi)有系統(tǒng)的學(xué)習(xí)過(guò)Vue的核心知識(shí)和API,你可能壓根就不知道有這樣的實(shí)現(xiàn)方式,所以想要學(xué)好Vue,系統(tǒng)得對(duì)Vue的核心進(jìn)行學(xué)習(xí)是非常重要的一個(gè)環(huán)節(jié)。
以上所述是小編給大家介紹的你不知道的Vue技巧之--開(kāi)發(fā)一個(gè)可以通過(guò)方法調(diào)用的組件詳解整合,希望對(duì)大家有所幫助,如果大家有任何疑問(wèn)請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!
相關(guān)文章
建立和維護(hù)大型 Vue.js 項(xiàng)目的 10 個(gè)最佳實(shí)踐
這篇文章小編要與大家分享的是建立和維護(hù)大型 Vue.js 項(xiàng)目的 10 個(gè)最佳實(shí)踐,需要的小伙伴請(qǐng)和小編一起學(xué)習(xí)下面文章的具體內(nèi)容吧2021-09-09vue實(shí)現(xiàn)列表滾動(dòng)的過(guò)渡動(dòng)畫(huà)
這篇文章主要為大家詳細(xì)介紹了vue實(shí)現(xiàn)列表滾動(dòng)的過(guò)渡動(dòng)畫(huà),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-06-06vue-router清除url地址欄路由參數(shù)的操作代碼
這篇文章主要介紹了vue-router清除url地址欄路由參數(shù),本文給大家介紹的非常詳細(xì),需要的朋友可以參考下2015-11-11Vue 實(shí)現(xiàn)v-for循環(huán)的時(shí)候更改 class的樣式名稱
這篇文章主要介紹了Vue 實(shí)現(xiàn)v-for循環(huán)的時(shí)候更改 class的樣式名稱,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-07-07vue element-ui實(shí)現(xiàn)el-table表格多選以及回顯方式
這篇文章主要介紹了vue element-ui實(shí)現(xiàn)el-table表格多選以及回顯方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-07-07在vue項(xiàng)目創(chuàng)建的后初始化首次使用stylus安裝方法分享
下面小編就為大家分享一篇在vue項(xiàng)目創(chuàng)建的后初始化首次使用stylus安裝方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-01-01Vue設(shè)置keepAlive不生效問(wèn)題及解決
這篇文章主要介紹了Vue設(shè)置keepAlive不生效問(wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-04-04