前言
近期閑來(lái)無(wú)事爱致,就想折騰一點(diǎn)東西哥放,無(wú)奈技術(shù)水平太低牍鞠,做不了什么高深復(fù)雜的項(xiàng)目咖摹,所以還是先從簡(jiǎn)單的做起吧。平時(shí)我們做項(xiàng)目的時(shí)候或多或少都會(huì)用到一些組件庫(kù)难述,比如element萤晴、muse之類的吐句,所以這次我想自己手動(dòng)寫(xiě)一個(gè)組件,封裝成一個(gè)包店读,可以暴露到外部去調(diào)用嗦枢。
效果預(yù)覽
廢話不多說(shuō),這次寫(xiě)的是一個(gè)Vue的消息彈窗組建屯断,借鑒了element的message組件文虏,大家可以去看一看element的源碼,里面一些組件的實(shí)現(xiàn)方式很巧妙殖演,非常值得去學(xué)習(xí)氧秘。話不多說(shuō),先來(lái)看看最終的實(shí)現(xiàn)效果:
代碼
第一步應(yīng)該是寫(xiě)一個(gè)模板:
<template>
<transition name="msg-fade" @after-leave="afterLeave">
<div :class="classes" class="msg-container" v-show="visible" role="alert" @mouseenter="clearTimer" @mouseleave="startTimer">
<span>{{ message }}</span>
<i class="msg-icon-wrapper" v-if="showCloseButton" @click="close">
<img class="msg-icon" src="./close.svg" alt="close" />
</i>
</div>
</transition>
</template>
可以看到趴久,html內(nèi)容是很簡(jiǎn)單的丸相,主要是幾個(gè)標(biāo)簽和一些props,還有幾個(gè)事件彼棍,然后我們?cè)賮?lái)看js:
<script>
export default {
name: 'Message',
props: {
// 要顯示的消息
message: {
type: String,
require: true
},
// 主題
type: {
type: String,
default: 'normal'
// normal success error warning
},
// 是否顯示關(guān)閉按鈕
showCloseButton: {
type: Boolean,
default: false
},
// 組件出現(xiàn)的位置
location: {
type: String,
default: 'top-center'
// top-center top-right top-left bottom-center bottom-right bottom-left
},
// 自定義樣式名
className: {
type: String,
default: ''
},
// 持續(xù)時(shí)間
duration: {
type: Number,
default: 2000
}
},
data () {
return {
// 動(dòng)態(tài)修改樣式
classes: [
{
'msg-icon-padding': this.showCloseButton
},
`msg-${this.type}`,
`msg-${this.location}`,
this.className
],
// 控制組件顯示
visible: false,
// 是否關(guān)閉
closed: false,
// 定時(shí)器
timer: null
}
},
watch: {
closed (newVal) {
if (newVal) {
this.visible = false
}
}
},
methods: {
close () {
this.closed = true
},
afterLeave () {
this.$destroy(true)
this.$el.parentNode.removeChild(this.$el)
},
clearTimer () {
clearTimeout(this.timer)
},
startTimer () {
const timeout = this.duration
if (timeout > 0) {
this.timer = setTimeout(() => {
if (!this.closed) {
this.close()
}
}, timeout);
}
}
},
mounted () {
this.startTimer()
this.visible = true
},
beforeDestroy () {
this.clearTimer()
}
}
</script>
可以看到組件的代碼邏輯也是非常簡(jiǎn)單的灭忠,props的處理我就不多細(xì)說(shuō)了,可能每一個(gè)人的需求都一樣座硕。重點(diǎn)要講一講的是組件的創(chuàng)建和銷毀過(guò)程更舞。在組件掛載后,組件會(huì)調(diào)用startTimer方法生成一個(gè)定時(shí)器坎吻,按照duration指定的時(shí)間后調(diào)用close方法缆蝉,close方法會(huì)設(shè)定data的closed為true,visible監(jiān)聽(tīng)到closed變?yōu)閠rue后就變?yōu)閒alse瘦真,所以根據(jù)v-show這個(gè)指令刊头,組件的display樣式屬性就會(huì)變成none。最后Vue官方內(nèi)置的過(guò)渡組件transition會(huì)調(diào)用一系列鉤子函數(shù)诸尽,我們直接在afterLeave這個(gè)鉤子上銷毀組件和清除dom原杂。看到這里您机,可能有同學(xué)會(huì)有疑問(wèn)了穿肄?為啥要這樣銷毀組件啊,直接用一個(gè)prop控制組件顯示不就可以了嗎际看?其實(shí)對(duì)于一般的組件我們是沒(méi)必要弄成這樣咸产,但是我們得思考一下消息彈窗的使用場(chǎng)景啊。一般情況下仲闽,消息彈窗都是在完成了某種操作后脑溢,彈出來(lái)告訴用戶操作成功與否,所以我們通常用js控制消息彈窗顯示赖欣。因此屑彻,我們的消息彈窗組件不僅僅是聲明式的验庙,更應(yīng)該是命令式的,直接用js控制顯示與否社牲。
很多同學(xué)應(yīng)該都使用過(guò)element粪薛,element在全局引入的時(shí)候會(huì)向vue上掛載一個(gè)message的方法,一般我們都是this.$message
這樣來(lái)操作的搏恤,我的目標(biāo)也是做成這樣子违寿。
import Vue from 'vue'
import Message from './Message.vue'
const messageConstructor = Vue.extend(Message)
let count = 1
const notify = (options = {}) => {
// 如果Vue運(yùn)行在服務(wù)端,放棄調(diào)用
if (Vue.prototype.$isServer) {
return
}
if (typeof options === 'string') {
options = {
message: options
}
}
// 創(chuàng)造實(shí)例
const instance = new messageConstructor({
propsData: options
})
// 渲染并掛載到body上
const vm = instance.$mount()
document.body.appendChild(vm.$el)
vm.$el.style.zIndex = count
// 返回一個(gè)主動(dòng)關(guān)閉的方法
const close = () => {
vm.closed = true
}
return close
}
export default notify
這里我們用了Vue的一個(gè)方法挑社,vue.extend
,這個(gè)方法接受一些配置參數(shù)巡揍,返回一個(gè)可以生產(chǎn)vue實(shí)例的構(gòu)造函數(shù)痛阻,其實(shí)我們平時(shí)使用的組件一般都是這些構(gòu)造函數(shù)實(shí)例化的對(duì)象。代碼的邏輯很簡(jiǎn)單腮敌,當(dāng)notify方法被調(diào)用的時(shí)候會(huì)創(chuàng)造一個(gè)message組件的實(shí)例對(duì)象阱当,同時(shí)傳入props,最后渲染成dom并掛載到document.body上糜工。最后我們提供一個(gè)install方法將notify和message組件全局安裝到Vue中:
class Plugin {
static install (Vue) {
Vue.prototype.notify = notify
Vue.mixin({
components: {
'oce-message': Message
}
})
}
}
export default Plugin
// 全局注冊(cè)
import oce-message from 'oce-message'
import Vue from 'vue'
Vue.use(oce-message)
// 使用
<oce-message ... />
this.notify(...)
好了弊添,一個(gè)簡(jiǎn)單的消息彈窗組件就完成了,具體的代碼和使用方法在這里oce-message可以找到捌木,歡迎star和提issue油坝。
后續(xù)
這個(gè)組件有點(diǎn)簡(jiǎn)陋,其實(shí)有一些可以優(yōu)化的地方:
- 樣式
- 過(guò)渡動(dòng)畫(huà)
- 性能(實(shí)例是否可以做成一個(gè)單例刨裆?)