通過vue.extend實現(xiàn)消息提示彈框的方法記錄
前提回顧
在項目開發(fā)中我們經(jīng)常使用的組件注冊分為兩種,一個是全局注冊和另一個是局部注冊,假設(shè)我們的業(yè)務(wù)場景是用戶在瀏覽注冊頁面時,點擊頁面中的注冊按鈕后,前端根據(jù)用戶的注冊信息先做一次簡單的驗證,并根據(jù)驗證彈出一個對應(yīng)消息提示彈框
我們拿到這個需求后,便開始著手準(zhǔn)備要通過局部注冊消息彈框組件的方法來實現(xiàn)這個場景,在通過局部注冊消息彈框組件的方法解決完這個需求后,自然是沾沾自喜,緊接著又迎來了一個需求,該需求是用戶在點擊該注冊按鈕時,點擊幾次就要出現(xiàn)幾次這個消息彈框,你開始犯了難,并思考難道我要在頁面中提前插入n個組件標(biāo)簽,不然我怎么知道用戶要點擊幾次注冊按鈕?
在你還沒有解決第二個需求的時候,又一個需求來了,第三個需求是不僅僅是注冊頁面需要用到這個消息彈框組件,在其他多個頁面中也需要用到這個消息彈框組件。
基于上述的業(yè)務(wù)需求,我們可以通過vue.extend編程式的使用組件,從而實現(xiàn)功能性的動態(tài)的消息提示彈框
局部注冊消息彈框組件
先通過局部注冊的方法來實現(xiàn)消息彈框組件
效果圖如下:

構(gòu)造目錄如下:

'src/main.js'文件的代碼:
import Vue from 'vue'
import App from './App.vue'
//全局引入樣式文件
import './assets/css.css';
Vue.config.productionTip = false
new Vue({
render: h => h(App)
}).$mount('#app')
'src/bus/bus.js'文件的代碼:
import vue from 'vue'; var bus=new vue() export default bus;
'src/App.vue'文件的代碼:
<template>
<div id="app">
<button @click="handleShowMessage">點擊出現(xiàn)彈框</button>
<TMessage :offsetTop='50'></TMessage>
<TMessage :offsetTop='100'></TMessage>
<TMessage :offsetTop='150'></TMessage>
<!-- 我是不是得在這里埋下幾萬個消息彈框組件??? -->
</div>
</template>
<script>
import TMessage from './components/TMessage/TMessage.vue';
import bus from './bus/bus';
export default {
name:'app',
data() {
return {
}
},
components: {
TMessage,
},
methods: {
handleShowMessage(){
//打印查看消息彈框的組件對象
console.log(TMessage);
//點擊按鈕后出現(xiàn)消息彈框
bus.$emit('showMessage')
}
},
}
</script>
<style>
#app {
display: flex;
justify-content: center;
}
#app button{
margin-top: 250px;
}
</style>
'src/components/TMessage/TMessage.vue'文件的代碼:
<template>
<transition name="message-fade">
<div :class="[
'message',
'message-' + type,
center ? 'is-center' : ''
]"
:style="{top: offset + 'px'}"
v-if="!closed"
>
<p class="message-content">提示信息:{{message}}</p>
<i class="icon icon-close"></i>
</div>
</transition>
</template>
<script>
export default {
name: 'TMessage',
data() {
return {
message: '這是默認信息', //彈框的提示內(nèi)容
type: 'success', //彈框的樣式 success、warning、error
center: true, //彈框是否居中顯示
offset: 20, //彈框默認的偏移量
closed: true, //彈框默認隱藏 通過v-if="!closed"控制
duration: 1000, //彈框消失的時間
timer: null, //準(zhǔn)備一個定時器
}
},
mounted() {
this.offset=this.offsetTop
bus.$on('showMessage',()=>{
this.closed=false;
this.timer = setTimeout(() => {
//如果彈框是顯示狀態(tài)的話在duration后會變?yōu)殡[藏狀態(tài)
if (!this.closed) {
this.close();
}
}, this.duration);
})
},
props:['offsetTop'],
methods: {
close() {
this.closed = true;
}
}
}
</script>
寫到這里,我們實現(xiàn)的效果為(動圖如下):

'src/assets/css.css'文件的代碼:
/*
樣式重點解析:
1.'message'
2.'message-' + type:
2.1:message-success
2.2:message-warning
2.3:message-error
3.'is-center' //決定了彈框居中顯示
4.'message-fade-enter' //4和5決定了彈框的的過渡效果
5.'message-fade-leave-active'
6. .message {
top:20px; //決定了彈框的偏移量
}
*/
.message {
min-width: 380px;
-webkit-box-sizing: border-box;
box-sizing: border-box;
border-radius: 4px;
border-width: 1px;
border-style: solid;
border-color: #EBEEF5;
position: fixed;
left: 50%;
top: 20px;
z-index: 999999999;
transform: translateX(-50%);
background-color: #edf2fc;
transition: opacity .3s, transform .4s, top .4s;
overflow: hidden;
padding: 15px 15px 15px 20px;
display: flex;
align-items: center
}
.message.is-center {
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center
}
.message p {
margin: 0
}
.message-info .message-content {
color: #909399
}
.message-success {
background-color: #f0f9eb;
border-color: #e1f3d8
}
.message-success .message-content {
color: #67C23A
}
.message-warning {
background-color: #fdf6ec;
border-color: #faecd8
}
.message-warning .message-content {
color: #E6A23C
}
.message-error {
background-color: #fef0f0;
border-color: #fde2e2
}
.message-error .message-content {
color: #F56C6C
}
.message-content {
padding: 0;
font-size: 14px;
line-height: 1
}
.message-content:focus {
outline-width: 0
}
.message .icon-close {
position: absolute;
top: 50%;
right: 15px;
-webkit-transform: translateY(-50%);
transform: translateY(-50%);
cursor: pointer;
color: #C0C4CC;
font-size: 16px
}
.message .icon-close:focus {
outline-width: 0
}
.message .icon-close:hover {
color: #909399
}
.message-fade-enter, .message-fade-leave-active {
opacity: 0;
transform: translate(-50%, -100%)
}
編程式的使用組件
上方在通過局部注冊消息彈框組件時體現(xiàn)的局限性:靈活性低、可復(fù)用性低、代碼觀感較差
緊接著我們就要使用vue.extend來實現(xiàn)消息提示彈框,做到編程式的使用組件
該構(gòu)造目錄為:

'src/App.vue'文件的代碼:
<template>
<div id="app">
<button @click="handleShowMessage">點擊出現(xiàn)彈框</button>
</div>
</template>
<script>
import Message from './components/TMessage/TMessage.js';
export default {
name:'app',
data() {
return {
}
},
methods: {
handleShowMessage(){
/**
* 每點擊一次按鈕就調(diào)用一次該工廠函數(shù)
* 每調(diào)用一次該工廠函數(shù)就創(chuàng)建一個彈框組件對象
*/
return Message('我好帥啊我好帥啊我好帥啊')
}
},
}
</script>
<style>
#app {
display: flex;
justify-content: center;
}
#app button{
margin-top: 250px;
}
</style>
'src/components/TMessage/TMessage.vue'文件的代碼:
<template>
<transition name="message-fade">
<div :class="[
'message',
'message-' + type,
center ? 'is-center' : ''
]"
:style="{top: offset + 'px'}"
v-if="!closed"
>
<p class="message-content">提示信息:{{message}}</p>
<i class="icon icon-close"></i>
</div>
</transition>
</template>
<script>
export default {
name: 'TMessage',
data() {
return {
message: '這是默認信息', //彈框的提示內(nèi)容
type: 'success', //彈框的樣式 success、warning、error
center: true, //彈框是否居中顯示
offset: 20, //彈框默認的偏移量
closed: false, //彈框默認隱藏 通過v-if="!closed"控制
duration: 1000, //彈框消失的時間
timer: null, //準(zhǔn)備一個定時器
}
},
mounted() {
/*為了方便演示先不讓彈框消失
this.timer = setTimeout(() => {
//在規(guī)定的this.duration后該消息彈框消失
if (!this.closed) {
this.close();
}
}, this.duration);
*/
},
methods: {
close() {
this.closed = true;
}
}
}
</script>
'src/components/TMessage/TMessage.js'文件的代碼:
import Vue from 'vue';
import TMessage from "./TMessage.vue";
function Message(data) {
data = data || {};
if (typeof data === 'string') {
data = {
message: data
}
}
const TMessageClass = Vue.extend(TMessage);
//得到的是一個組件對象VueComponent實例
//new TMessageClass接收的是一個包含組件選項的對象 覆蓋
let instance = new TMessageClass({
data
});
instance.$mount();
console.log(instance.$el,'現(xiàn)在才可以訪問$el');
/* instance.$el的打印結(jié)果如下:
<div class="message message-success is-center" style="top: 20px;">
<p class="message-content">提示信息:我好帥啊我好帥啊我好帥啊</p>
<i class="icon icon-close"></i>
</div>
*/
document.body.appendChild(instance.$el);
}
export default Message
寫到這里,我們來看一下效果,如下圖:


解決消息彈框覆蓋問題
我們已經(jīng)做到了每點擊一次按鈕就出現(xiàn)一個消息彈框組件,但是因為定位的問題出現(xiàn)了相互覆蓋,所以得再接著去'TMessage.js'文件中去完善邏輯:
//file:'src/components/TMessage/TMessage.js'
/*
解決方法:
通過維護一個隊列來存儲每一個消息彈框組件對象
在每一次生成消息彈框組件時都需要重新計算其top值
通過該隊列來計算上一個消息彈框組件對象的top值
*/
import Vue from 'vue';
import TMessage from "./TMessage.vue";
//裝有instance消息彈框組件對象的容器
let instances = [];
function Message(data) {
data = data || {};
if (typeof data === 'string') {
data = {
message: data
}
}
data.onClose = function() {
console.log('onClose');
// 每消失一個消息彈框就會觸發(fā)一個onClose
//instance是消息彈框組件的實例對象即VueComponent
console.log(instance,'instance');
//每消失一個就得把在instances容器中對應(yīng)的該組件對象給刪除掉
Message.close(instance);
};
const TMessageClass = Vue.extend(TMessage);
let instance = new TMessageClass({
data
});
instance.$mount();
document.body.appendChild(instance.$el);
//如果data數(shù)據(jù)中有設(shè)置偏移量則使用該偏移量
//否則使用默認的偏移量值20
let offset = data.offset || 20;
//規(guī)定每一個消息彈框的間隔
//這里直接使用offset值做為間隔
let offsetTop = offset;
/**思路如下:
* let offsetTop=20;
* [].forEach(()=>{offsetTop+=10});
* console.log(offsetTop) //還是20
*
* [{a:'1'}].forEach(()=>{offsetTop+=10});
* console.log(offsetTop) //才是30
*/
/*這里是在循環(huán)之后才去push
因為生成的第一個消息彈框是不需要計算offsetTop的
生成的第一個消息彈框直接使用offset值即可
*/
//從第一個起instances里有值了(組件對象)以后再去循環(huán)計算offsetTop值
instances.forEach( item => {
//根據(jù)上一個計算的offsetTop+自身的高度+規(guī)定的間隔
offsetTop += item.$el.offsetHeight + offset;
});
//當(dāng)前生成的消息彈框的高度為offsetTop
//offsetTop是根據(jù)上一個生成的消息彈框的三個值計算得到的
//instances容器中第0個是不需要參與計算的即采用默認的offset值
instance.$el.style.top = offsetTop + 'px';
instances.push(instance);
}
Message.close = function(instance) {
//每消失一個就得把在instances容器中對應(yīng)的該組件對象給刪除掉
instances = instances.filter( item => item !== instance );
};
export default Message
'src/components/TMessage/TMessage.vue'文件的代碼:
//file:'src/components/TMessage/TMessage.vue'
<template>
<transition name="message-fade">
<div :class="[
'message',
'message-' + type,
center ? 'is-center' : ''
]"
:style="{top: offset + 'px'}"
v-if="!closed"
>
<p class="message-content">提示信息:{{message}}</p>
<i class="icon icon-close"></i>
</div>
</transition>
</template>
<script>
export default {
name: 'TMessage',
data() {
return {
message: '這是默認信息', //彈框的提示內(nèi)容
type: 'success', //彈框的樣式 success、warning、error
center: true, //彈框是否居中顯示
offset: 20, //彈框默認的偏移量
closed: false, //彈框默認隱藏 通過v-if="!closed"控制
duration: 1000, //彈框消失的時間
timer: null, //準(zhǔn)備一個定時器,
onClose: null //擴充一個功能 彈框消失后觸發(fā)
}
},
mounted() {
//在規(guī)定的this.duration后該消息彈框消失
//消息框消失后會觸發(fā)this.close()函數(shù)方法
this.timer = setTimeout(() => {
if (!this.closed) {
this.close();
}
}, this.duration);
},
methods: {
close() {
this.closed = true;
//如果該組件可以接收到this.onClose方法則調(diào)用該方法
//該方法是在該消息彈框消失的時候被觸發(fā)
if (typeof this.onClose === 'function') {
this.onClose();
}
}
}
}
</script>
寫到這里,我們來看一下效果,如下圖:

優(yōu)化消息彈框消失的效果
我們可以進一步的優(yōu)化消息彈框消失的效果,效果圖如下:

'src/components/TMessage/TMessage.vue'文件的代碼:
<template>
<transition name="message-fade">
<div :class="[
'message',
'message-' + type,
center ? 'is-center' : ''
]"
:style="{top: offset + 'px'}"
v-if="!closed"
>
<p class="message-content">提示信息:{{message}}</p>
<i class="icon icon-close"></i>
</div>
</transition>
</template>
<script>
export default {
name: 'TMessage',
data() {
return {
message: '這是默認信息',
type: 'success',
center: true,
offset: 20,
closed: false,
duration: 1000,
timer: null,
onClose: null //擴充一個功能 彈框消失后觸發(fā)
}
},
mounted() {
this.timer = setTimeout(() => {
if (!this.closed) {
this.close();
}
}, this.duration);
},
methods: {
close() {
this.closed = true;
//當(dāng)彈框消失時會調(diào)用this.onClose()該函數(shù)方法
if (typeof this.onClose === 'function') {
this.onClose();
}
}
}
}
</script>
'src/components/TMessage/TMessage.js'文件的代碼:
import Vue from 'vue';
import TMessage from "./TMessage.vue";
//裝有instance的容器
let instances = [];
function Message(data) {
data = data || {};
if (typeof data === 'string') {
data = {
message: data
}
}
const TMessageClass = Vue.extend(TMessage);
let instance = new TMessageClass({
data
});
instance.$mount();
document.body.appendChild(instance.$el);
data.onClose = function() {
console.log('onClose');
// 每消失一個彈框就會觸發(fā)一個onClose方法
Message.close(instance);
};
//如果data數(shù)據(jù)中有設(shè)置偏移量則使用該偏移量
//否則使用默認的偏移量20
let offset = data.offset || 20;
//規(guī)定每一個消息彈框的間隔
let offsetTop = offset;
instances.forEach( item => {
//上一個實例對象的offsetTop+自身的高度+規(guī)定的間隔
offsetTop += item.$el.offsetHeight + offset;
});
instance.$el.style.top = offsetTop + 'px';
instances.push(instance);
}
Message.close = function(instance) {
/*
每次彈窗關(guān)閉都會調(diào)用一次這個函數(shù)
* 獲取當(dāng)前這個instance的高度
* 把這個instance后面的所有實例的top減去這個高度,再減去偏移
* */
let removeHeight = instance.$el.offsetHeight + instance.offset;
//把傳遞進來的instance在容器instances中刪除
let index = instances.findIndex( item => item === instance );
instances = instances.filter( item => item !== instance );
//對應(yīng)的消息彈框消失后在該消息彈框后面的消息彈框會依次出現(xiàn)頂上來的效果
//原理是找到對應(yīng)的消息彈框在instances容器中的下標(biāo)位置
//通過循環(huán)改變對應(yīng)的消息彈框后面的所有消息彈框的高度
for (let i = index; i<instances.length; i++) {
instances[i].$el.style.top = parseFloat(instances[i].$el.style.top) - removeHeight + 'px';
}
};
export default Message
'src/App.vue'文件的代碼:
<template>
<div id="app">
<button @click="handleShowMessage">點擊出現(xiàn)彈框</button>
</div>
</template>
<script>
import Message from './components/TMessage/TMessage.js';
export default {
name:'app',
data() {
return {
}
},
methods: {
handleShowMessage(){
/**
* 調(diào)用一次就創(chuàng)建一個彈框組件對象
*/
return Message('我好帥啊我好帥啊我好帥啊')
}
},
}
</script>
<style>
#app {
display: flex;
justify-content: center;
}
#app button{
margin-top: 250px;
}
</style>
終極版實現(xiàn)版
我們在上方'src/App.vue'文件中是通過引入TMessage.js后再通過Message()的方式調(diào)用使用該組件的,還可以將調(diào)用方式掛載到Vue全局上,來看看怎么操作:
'src/main.js'文件的代碼
import Vue from 'vue'
import App from './App.vue'
import './assets/css.css';
import Message from '../src/components/TMessage/TMessage';
Vue.config.productionTip = false
//掛載到全局
Vue.prototype.$message = Message;
new Vue({
render: h => h(App)
}).$mount('#app')
'src/App.vue'文件的代碼
<template>
<div id="app">
<button @click="handleShowMessage">點擊出現(xiàn)彈框</button>
</div>
</template>
<script>
export default {
name:'app',
data() {
return {
}
},
methods: {
handleShowMessage(){
this.$message.error('我好帥啊我好帥啊我好帥啊')
this.$message.success('我好帥啊我好帥啊我好帥啊')
this.$message.info('我好帥啊我好帥啊我好帥啊')
this.$message.warning('我好帥啊我好帥啊我好帥啊')
}
},
}
</script>
<style>
#app {
display: flex;
justify-content: center;
}
#app button{
margin-top: 250px;
}
</style>
'src/components/TMessage/TMessage.vue'文件的代碼:
<template>
<transition name="message-fade">
<div :class="[
'message',
'message-' + type,
center ? 'is-center' : ''
]"
:style="{top: offset + 'px'}"
v-if="!closed"
>
<p class="message-content">提示信息:{{message}}</p>
<i class="icon icon-close"></i>
</div>
</transition>
</template>
<script>
export default {
name: 'TMessage',
data() {
return {
message: '這是默認信息',
type: 'success',
center: true,
offset: 20,
closed: false,
duration: 1000,
timer: null,
onClose: null //擴充一個功能 彈框消失后觸發(fā)
}
},
mounted() {
this.timer = setTimeout(() => {
if (!this.closed) {
this.close();
}
}, this.duration);
},
methods: {
close() {
this.closed = true;
//當(dāng)彈框消失時會調(diào)用this.onClose()該函數(shù)方法
if (typeof this.onClose === 'function') {
this.onClose();
}
}
}
}
</script>
'src/components/TMessage/TMessage.js'文件的代碼:
import Vue from 'vue';
import TMessage from "./TMessage.vue";
let instances = [];
function Message(data) {
data = data || {};
if (typeof data === 'string') {
data = {
message: data
}
}
data.onClose = function() {
console.log('onClose');
//instance是消息彈框組件的實例對象即VueComponent
Message.close(instance);
};
const TMessageClass = Vue.extend(TMessage);
let instance = new TMessageClass({
data
});
instance.$mount();
// console.log(instance.$el,'現(xiàn)在才可以訪問$el');
document.body.appendChild(instance.$el);
let offset = data.offset || 20;
//規(guī)定每一個消息彈框的間隔
let offsetTop = offset;
//第一個彈框是不需要計算偏移量的
//從第一個以后再去循環(huán)
instances.forEach( item => {
//上一個實例對象的offsetTop+自身的高度+規(guī)定的間隔
offsetTop += item.$el.offsetHeight + offset;
});
instance.$el.style.top = offsetTop + 'px';
instances.push(instance);
}
Message.close = function(instance) {
let removeHeight = instance.$el.offsetHeight + instance.offset;
let index = instances.findIndex( item => item === instance );
instances = instances.filter( item => item !== instance );
for (let i = index; i<instances.length; i++) {
instances[i].$el.style.top = parseFloat(instances[i].$el.style.top) - removeHeight + 'px';
}
};
['info', 'success', 'error', 'warning'].forEach( type => {
Message[type] = function(data) {
if (typeof data === 'string') {
data = {
message: data
}
}
data.type = type;
//整合data后再次去調(diào)用Message()
return Message(data);
};
} );
// Message.error=function(data){
// if (typeof data === 'string') {
// data = {
// message: data
// }
// }
// return Message({
// ...data,
// type:'error'
// })
// }
export default Message
完結(jié)撒花,最后來看一下效果圖:

總結(jié)
到此這篇關(guān)于通過vue.extend實現(xiàn)消息提示彈框的文章就介紹到這了,更多相關(guān)vue.extend實現(xiàn)消息提示彈框內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Vue3實現(xiàn)canvas畫布組件自定義畫板實例代碼
Vue?Canvas是一個基于Vue.js的輕量級畫板組件,旨在提供一個簡易的畫布功能,用戶可以在網(wǎng)頁上進行自由繪圖,文中通過代碼介紹的非常詳細,需要的朋友可以參考下2024-09-09
使用vue插件axios傳數(shù)據(jù)的Content-Type及格式問題詳解
這篇文章主要介紹了使用vue插件axios傳數(shù)據(jù)的Content-Type以及格式問題,本文通過實例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-09-09
vue elementui el-form rules動態(tài)驗證的實例代碼詳解
在使用elementUI el-form 中,對于業(yè)務(wù)不同的時候可能會產(chǎn)生不同表單結(jié)構(gòu),但是都是存在同一個表單控件el-form中。這篇文章主要介紹了vue elementui el-form rules動態(tài)驗證的實例代碼,需要的朋友可以參考下2019-05-05
vue+element+springboot實現(xiàn)文件下載進度條展現(xiàn)功能示例
本文主要介紹了vue + element-ui + springboot 實現(xiàn)文件下載進度條展現(xiàn)功能,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-11-11
vue+mockjs模擬數(shù)據(jù)實現(xiàn)前后端分離開發(fā)的實例代碼
本篇文章主要介紹了vue+mockjs模擬數(shù)據(jù)實現(xiàn)前后端分離開發(fā)的實例代碼,具有一定的參考價值,有興趣的可以了解一下2017-08-08

