過年了,基于Vue做一個消息通知組件
前言
今天除夕,在這里祝大家新年快樂!??!今天在這個特別的日子里我們做一個消息通知組件,好,我們開始行動起來吧?。?!
項目一覽
效果很簡單,就是這種的小卡片似的效果。
我們先開始寫UI頁面,可自定義消息內(nèi)容以及關(guān)閉按鈕的樣式。
Notification.vue
<template>
<transition name="fade" @after-enter="handleAfterEnter">
<div class="notification" :style="style" v-show="visible">
<span class="notification__content">
{{content}}
</span>
<span class="notification__btn" @click="handleClose">{{btn}}</span>
</div>
</transition>
</template>
<script>
export default {
name: 'Notification',
props: {
content: {
type: String,
required: true
},
btn: {
type: String,
default: 'close'
}
}
}
</script>
<style lang="less" scoped>
.fade-enter-active, .fade-leave-active{
transition: opacity 1s;
}
.fade-enter, .fade-leave-to{
opacity: 0;
transform: translateX(100px);
}
.notification{
display: flex;
background-color: #303030;
color: rgba(255, 255, 255, 1);
align-items: center;
padding: 20px;
position: fixed;
min-width: 280px;
box-shadow: 0 3px 5px -1px rgba(0, 0, 0, 0.2), 0px 6px 10px 0px rgba(0, 0, 0, 0.3);
flex-wrap: wrap;
transition: all 0.3s;
border-radius:10px ;
&__content{
padding: 0;
}
&__btn{
color: #ff4081;
padding-left: 24px;
margin-left: auto;
cursor: pointer;
}
}
</style>
寫完基本的樣式組件,我們該賦予其靈魂了。好,我們開始寫最重要的邏輯。
notify.js
import Vue from "vue";
import Notification from "./Notification.vue";
const NotificationConstructor = Vue.extend(Notification);
const instances = [];
let seed = 1;
// 消除Vue實例
const removeInstance = (instance) => {
if (!instance) return;
const len = instances.length;
const index = instances.findIndex((ins) => instance.id === ins.id);
instances.splice(index, 1);
if (len <= 1) return;
const removeHeight = instance.height;
for (let i = index; i < len - 1; i++) {
instances[i].verticalOffset =
parseInt(instances[i].verticalOffset) - removeHeight - 16;
}
};
const notify = (options = {}) => {
if (Vue.prototype.$isServer) return;
// 獲取vue實例
let instance = new NotificationConstructor({
propsData: options, // 這里是傳進來一組props
data() {
return {
verticalOffset: 0,
timer: null,
visible: false,
height: 0,
};
},
computed: {
// 配置消息組件的位置
style() {
return {
position: "fixed",
right: "20px",
bottom: `${this.verticalOffset}px`,
};
}
},
mounted() {
this.createTimer();
this.$el.addEventListener("mouseenter", () => {
if (this.timer) {
this.clearTimer(this.timer);
}
});
this.$el.addEventListener("mouseleave", () => {
if (this.timer) {
this.clearTimer(this.timer);
}
this.createTimer();
});
},
updated() {
this.height = this.$el.offsetHeight;
},
beforeDestroy() {
this.clearTimer();
},
methods: {
// 創(chuàng)建計時器
createTimer() {
this.timer = setTimeout(() => {
this.visible = false;
document.body.removeChild(this.$el);
removeInstance(this);
this.$destroy();
}, options.timeout || 5000);
},
// 清除計時器
clearTimer() {
if (this.timer) {
clearTimeout(this.timer);
}
},
// 關(guān)閉消息彈窗
handleClose() {
this.visible = false;
document.body.removeChild(this.$el);
removeInstance(this);
this.$destroy(true);
},
// 過渡js鉤子
handleAfterEnter() {
this.height = this.$el.offsetHeight;
},
},
});
const id = `notification_${seed++}`; // 動態(tài)生成唯一Id
instance.id = id;
// 生成vue中的$el
instance = instance.$mount();
// 將$el中的內(nèi)容插入dom節(jié)點中去
document.body.appendChild(instance.$el);
instance.visible = true;
let verticalOffset = 0;
instances.forEach((item) => {
verticalOffset += item.$el.offsetHeight + 16;
});
verticalOffset += 16;
instance.verticalOffset = verticalOffset;
instances.push(instance);
return instance;
};
export default notify;
當(dāng)消息組件組高度超過瀏覽器顯示區(qū)域的時候,消息組件會依次按順序消失。
在這里,我們使用了Vue.extend(),在這里我們簡單地介紹下,官網(wǎng)上是這樣介紹的。
使用基礎(chǔ) Vue 構(gòu)造器,創(chuàng)建一個“子類”。參數(shù)是一個包含組件選項的對象。
data 選項是特例,需要注意 - 在 Vue.extend() 中它必須是函數(shù)
<div id="app"></div>
// 創(chuàng)建構(gòu)造器
var Profile = Vue.extend({
template: '<p>{{firstName}} {{lastName}} aka {{alias}}</p>',
data: function () {
return {
firstName: 'Walter',
lastName: 'White',
alias: 'Heisenberg'
}
}
})
// 創(chuàng)建 Profile 實例,并掛載到一個元素上。
new Profile().$mount('#app')
@after-enter="handleAfterEnter",看到這很多小伙伴會有疑問,其實這是Vue過渡組件中 JavaScript 鉤子。
官網(wǎng)的解釋這樣講。
可以在 attribute 中聲明 JavaScript 鉤子
<transition
v-on:before-enter="beforeEnter"
v-on:enter="enter"
v-on:after-enter="afterEnter"
v-on:enter-cancelled="enterCancelled"
v-on:before-leave="beforeLeave"
v-on:leave="leave"
v-on:after-leave="afterLeave"
v-on:leave-cancelled="leaveCancelled"
>
<!-- ... -->
</transition>
// ...
methods: {
// --------
// 進入中
// --------
beforeEnter: function (el) {
// ...
},
// 當(dāng)與 CSS 結(jié)合使用時
// 回調(diào)函數(shù) done 是可選的
enter: function (el, done) {
// ...
done()
},
afterEnter: function (el) {
// ...
},
enterCancelled: function (el) {
// ...
},
// --------
// 離開時
// --------
beforeLeave: function (el) {
// ...
},
// 當(dāng)與 CSS 結(jié)合使用時
// 回調(diào)函數(shù) done 是可選的
leave: function (el, done) {
// ...
done()
},
afterLeave: function (el) {
// ...
},
// leaveCancelled 只用于 v-show 中
leaveCancelled: function (el) {
// ...
}
}
這些鉤子函數(shù)可以結(jié)合 CSS transitions/animations 使用,也可以單獨使用。
當(dāng)只用 JavaScript 過渡的時候,在 enter 和 leave 中必須使用 done 進行回調(diào)。否則,它們將被同步調(diào)用,過渡會立即完成。
推薦對于僅使用 JavaScript 過渡的元素添加 v-bind:css="false",Vue 會跳過 CSS 的檢測。這也可以避免過渡過程中 CSS 的影響。
詳情解釋可以查看官方文檔
整理完UI組件與邏輯文件,下一步做得工作是將它們整合起來,通過Vue命令的方式直接使用。
index.js
import Notification from "./Notification.vue";
import notify from "./notify.js";
export default (Vue) => {
Vue.component(Notification.name, Notification);
Vue.prototype.$notify = notify;
};
下面,我們將使用它。
在main.js中引入index.js文件。
import Notification from "../src/components/notification/index.js";
Vue.use(Notification);
然后,你在相應(yīng)的組件中這樣調(diào)用它即可。
this.$notify({
content: "Hello World", // 消息內(nèi)容
btn: "關(guān)閉" // 關(guān)閉按鈕內(nèi)容
});
作者:Vam的金豆之路
主要領(lǐng)域:前端開發(fā)
我的微信:maomin9761
微信公眾號:前端歷劫之路