vue全局彈窗通用解決方案——組件函數(shù)化

我們?cè)趘ue項(xiàng)目開(kāi)發(fā)過(guò)程中總是遇到各種個(gè)樣的全局彈窗(權(quán)限提醒磅叛,alert提示蔗衡,繳費(fèi)提醒),這些全局彈窗全站很多地方在調(diào)用嘀韧,調(diào)用調(diào)用很蠻煩江解,將這些組件部署到全局位置,又無(wú)法做到按需加載琅拌,而且部署到全局的組件數(shù)據(jù)回傳(比如檢測(cè)繳費(fèi)是否完成)又是一個(gè)問(wèn)題,我們又得借助vuex或者event來(lái)傳遞,又回造成vuex的濫用掂林。

我們舉一個(gè)簡(jiǎn)單的場(chǎng)景:
如果我們要在vue里實(shí)現(xiàn)一個(gè)類似原生alert的彈出框,大概長(zhǎng)這個(gè)樣子坝橡。

<template>
    <div class="popup_background">
        <div>
            <div v-html="title"></div>
            <button @click="ok">確定</button>
            <button @click="cancel">否定</button>
        </div>
    </div>
</template>
<script>
export default {
    props: ['text'],
    data() {
        return {};
    },
    methods: {
        ok() {
            this.$emit('ok');
        },
        cancel() {
            this.$emit('cancel');
        }
    }
}
</script>

如果有一個(gè)頁(yè)面泻帮,需要使用這個(gè)組件,即使是空頁(yè)面计寇,也要做下面代碼里描述的所有事情

<template>
    <div>
        <button @click="showAlert">打開(kāi)alert</button>
        <ui-alert v-if="isShow" title="這是標(biāo)題" @ok="fine" @cancel="cancel"></ui-alert>
    </div>
</template>
<script>
import uiAlert from '******.vue';  // 引用組件
export default {
    data() {
        return {
            isShow:false  //控制彈窗是否顯示的開(kāi)關(guān)
        };
    },
    methods: {
        showAlert() {  //控制彈窗顯示
            this.isShow = true;
        },
        fine() {  //彈窗打開(kāi)后點(diǎn)擊ok的事件綁定
            this.isShow = false;
            // 點(diǎn)擊ok觸發(fā)的事情
        },
        cancel() {  //彈窗打開(kāi)后點(diǎn)擊cancel的事件綁定
            this.isShow = false;
            // 點(diǎn)擊cancel觸發(fā)的事情
        }
    },
    components:{uiAlert}
}
</script>

匯總一下锣杂,做的事情有
1脂倦、引用組件
2、設(shè)置控制顯示的變量開(kāi)關(guān)
3元莫、設(shè)置彈出組件的函數(shù)
4赖阻、設(shè)置多個(gè)回調(diào)事件,監(jiān)聽(tīng)返回值

但是我們想一下踱蠢,假如我們可以像調(diào)用原生組件類似的形式火欧,一個(gè)函數(shù)搞定,會(huì)更方便茎截。獲取返回值的方式比如promise苇侵、回調(diào)。比如我們封裝成返回promise的一個(gè)函數(shù)企锌,代碼可能像下面這樣榆浓。

<template>
    <div>
        <button @click="showAlert">打開(kāi)alert</button>
    </div>
</template>
<script>
import popAlert from '*******';
export default {
    methods: {
        showAlert() {
              popAlert('這是標(biāo)題').then(res=>{
                      // res 就是true或者false,代表點(diǎn)擊了ok還是cancel
              })
        }
    }
}
</script>

這樣調(diào)用起來(lái)很方便霎俩,我們可以看看類似alert這樣的彈出框哀军,有一些特點(diǎn),就是過(guò)程化打却,它從被創(chuàng)建到使用杉适,再到最后銷毀,這個(gè)過(guò)程相對(duì)調(diào)用的頁(yè)面是獨(dú)立的柳击,交互過(guò)程是高度過(guò)程化的猿推。不像其他組件和頁(yè)面直接可能多次交互。簡(jiǎn)單來(lái)說(shuō)捌肴,就是“用完即刪”蹬叭。再有就是布局獨(dú)立,布局層面跟調(diào)用者不存在占位歸屬状知,更像是全局的組件秽五。
只要符合這種特點(diǎn)的組件,封裝成函數(shù)饥悴,是更方便的策略坦喘。

那么如何封裝出一個(gè)類似剛才例子中popAlert這樣的函數(shù)呢?我們要保留組件要使用vue的開(kāi)發(fā)方式西设,也就是說(shuō)瓣铣,我們要把已經(jīng)存在的vue組件不進(jìn)行任何改動(dòng),在組件外對(duì)它通過(guò)包裝的形式形成一個(gè)函數(shù)贷揽。這樣vue組件可以向前兼容棠笑,并且組件繼續(xù)使用vue的開(kāi)發(fā)方式。

全局按需動(dòng)態(tài)加載

我想到的實(shí)現(xiàn)方式禽绪,就是利用render函數(shù)蓖救。我們?cè)谝粋€(gè)“全局”的地方洪规,所有頁(yè)面都會(huì)加載的最外層的模板,比如跟路由配置的那個(gè)App.vue循捺。在這里添加我們要開(kāi)發(fā)的淹冰,可以動(dòng)態(tài)改變狀態(tài)的組件。

<template>
    <div id="app">
        <router-view/>
        <!--全局的地方添加一個(gè)組件-->
        <dynamic-vue></dynamic-vue>
    </div>
</template>
.........

我們看一下dynamic-vue的實(shí)現(xiàn)

<!--一個(gè)用于創(chuàng)建臨時(shí)vue對(duì)象的容器巨柒,例如選擇彈窗樱拴,提示窗,或其他場(chǎng)景下使用的"用完即刪"型需求-->
<script>
export default {
    data() {
        return {
            globleVue: null // 臨時(shí)動(dòng)態(tài)添加的窗體洋满,例如彈窗等
            /*
            {
                vueObj: 'vue組件',
                attr:{  //初始化組件的props
                },
                on:{  //組件的emit回調(diào)綁定
                }
            }
            */
        }
    },
    render(createElement) {
        if(this.globleVue){
            return createElement(this.globleVue.vueObj, {
                props: this.globleVue.attr,
                on: this.globleVue.event
            });
        }
        else {
            return createElement('div');
        }
    }
}
</script>

接下來(lái)我們只要能夠通過(guò)某些方法把globleVue賦值成包含vue組件的對(duì)象晶乔,初始化的參數(shù),回調(diào)方法的對(duì)象牺勾,全局就會(huì)彈出這個(gè)組件正罢。我的實(shí)現(xiàn)方式是使用自帶的events對(duì)globleVue進(jìn)行修改。我們?cè)赾reated里面增加event的監(jiān)聽(tīng)驻民。

<script>
import { dynamicVueEvent } from 'dynamicVueObject.js';
export default {
    created() {
        // 支持全局調(diào)用臨時(shí)添加翻具,用完即刪型vue組件
        dynamicVueEvent.on('addVueObj', (vueObj, attr, event) => {
            this.globleVue = {
                vueObj,
                attr,
                event
            };
        });
        dynamicVueEvent.on('deleteVueObj', () => {
            this.globleVue = null;
        });
    },
    data() {
        return {
            globleVue: null
        }
    },
    render(createElement) {
        if(this.globleVue){
            return createElement(this.globleVue.vueObj, {
                props: this.globleVue.attr,
                on: this.globleVue.event
            });
        }
        else {
            return createElement('div');
        }
    }
}
</script>

觸發(fā) dynamicVueEvent 事件的代碼。

// dynamicVueObject.js
import Vue from 'vue';
var events = require('events');
var eventEmitter = new events.EventEmitter();
export function createVueObj(vueClass, attr, event) {
    let vueObj = Vue.extend(vueClass);
    dynamicVueEvent.emit('addVueObj', vueObj, attr, event);
}
export function deleteVueObj() {
    dynamicVueEvent.emit('deleteVueObj');
}

dynamicVueObject.js暴露了兩個(gè)函數(shù)回还,一個(gè)動(dòng)態(tài)創(chuàng)建組件createVueObj裆泳,一個(gè)清空組件deleteVueObj。我們就有了在全局動(dòng)態(tài)控制彈出組件的這個(gè)功能柠硕。我們可以在任何地方通過(guò)這兩個(gè)函數(shù)控制全局彈窗了工禾。

接下來(lái)我們就可以對(duì)已經(jīng)存在的alert.vue組件,不進(jìn)行修改的基礎(chǔ)上蝗柔,單獨(dú)封裝一個(gè)獨(dú)立的函數(shù)闻葵。


image.png

我們?cè)谠嫉腶lert.vue旁邊創(chuàng)建一個(gè)alert.js,這樣調(diào)用者很容易發(fā)現(xiàn)和調(diào)用癣丧。

/*
    alert.js
    alert.vue組件函數(shù)化
*/
// 引入全局創(chuàng)建組件槽畔,和全局刪除組件兩個(gè)方法
import { createVueObj, deleteVueObj } from '../dynamicVueObject/dynamicVueObject';
import alertVue from './alert.vue'; // 引入要使用的組件

export function alert(text) {
    return new Promise((resolve, reject) => {
        let dyVueObj = createVueObj(alertVue, {
            text
        }, {
            ok() {
                resolve(true);
                deleteVueObj(dyVueObj); // 刪除掉彈出的組件
            },
            cancel() {
                resolve(false);
                deleteVueObj(dyVueObj); // 刪除掉彈出的組件
            }
        });
    });
}

這樣alert函數(shù)封裝完畢了。不到20行就可以針對(duì)alert.vue封裝一個(gè)promise方式的alert胁编,很方便厢钧,接下來(lái)我們就可以調(diào)用一下這個(gè)alert函數(shù)

<template>
    <div>
        <button @click="showAlert">打開(kāi)alert</button>
    </div>
</template>
<script>
import popAlert from '****/alert.js';
export default {
    methods: {
        showAlert() {
              popAlert('這是標(biāo)題').then(res=>{
                      // res 就是true或者false,代表點(diǎn)擊了ok還是cancel
              })
        }
    }
}
</script>

這樣調(diào)用就方便多了L秃簟;悼臁铅檩!
大概實(shí)現(xiàn)邏輯就是如此了憎夷。

后續(xù)做的優(yōu)化

1、不再使用手動(dòng)部署dynamicVueObject.vue到一個(gè)全局的地方昧旨。

不再使用手動(dòng)部署dynamicVueObject.vue到一個(gè)全局的地方拾给,而是在dynamicVueObject.js第一次執(zhí)行的時(shí)候祥得,在body上添加一個(gè)動(dòng)態(tài)創(chuàng)建的新div上。

let dyVueObj = Vue.extend({
        /*****dynamicVueObject.vue******/
});
// 添加到一個(gè)動(dòng)態(tài)的div上
let instance = new dyVueObj({
    el: document.createElement('div'),
});
// 添加到body上
document.body.appendChild(instance.$el);

export function createVueObj(vueClass, attr, event) {
    let vueObj = Vue.extend(vueClass);
    dynamicVueEvent.emit('addVueObj', vueObj, attr, event);
    return vueObj;
}

export function deleteVueObj(obj) {
    dynamicVueEvent.emit('deleteVueObj', obj);
}

2蒋得、支持同時(shí)彈出多個(gè)組件

allGlobleVue改為了一個(gè)數(shù)組级及,支持添加多個(gè)彈出組件createVueObj方法的返回值,作為deleteVueObj刪除方法的參數(shù)额衙。

let dyVueObj = Vue.extend({
    created() {
        // 支持全局調(diào)用臨時(shí)添加饮焦,用完即刪型vue組件
        dynamicVueEvent.on('addVueObj', (vueObj, attr, event) => {
            this.allGlobleVue.push({
                vueObj, attr, event
            });
        });
        dynamicVueEvent.on('deleteVueObj', (vueObj) => {
            this.allGlobleVue.splice(this.allGlobleVue.findIndex(item => {
                return item.vueObj === vueObj
            }), 1);
        });
    },
    data() {
        return {
            allGlobleVue: [] // 臨時(shí)動(dòng)態(tài)添加的窗體,例如彈窗等
        }
    },
    render(createElement) {
        return createElement('div', {}, this.allGlobleVue.map(item => {
            return createElement(item.vueObj, {
                props: item.attr,
                on: item.event
            })
        }));
    }
});

完整代碼的git倉(cāng)庫(kù)是
https://github.com/13601313270/dynamicVueObject
完整的功能窍侧,支持同時(shí)彈出多個(gè)組件县踢,git倉(cāng)庫(kù)里包含一個(gè)可以跑起來(lái)的demo。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末伟件,一起剝皮案震驚了整個(gè)濱河市硼啤,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌斧账,老刑警劉巖谴返,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異咧织,居然都是意外死亡嗓袱,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門习绢,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)索抓,“玉大人,你說(shuō)我怎么就攤上這事毯炮”瓶希” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵桃煎,是天一觀的道長(zhǎng)篮幢。 經(jīng)常有香客問(wèn)我为迈,道長(zhǎng),這世上最難降的妖魔是什么葫辐? 我笑而不...
    開(kāi)封第一講書人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮耿战,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己狈涮,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布歌馍。 她就那樣靜靜地躺著,像睡著了一般松却。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上晓锻,一...
    開(kāi)封第一講書人閱讀 51,631評(píng)論 1 305
  • 那天肉渴,我揣著相機(jī)與錄音,去河邊找鬼带射。 笑死同规,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的窟社。 我是一名探鬼主播券勺,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼灿里!你這毒婦竟也來(lái)了关炼?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤匣吊,失蹤者是張志新(化名)和其女友劉穎儒拂,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體色鸳,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡社痛,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了命雀。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蒜哀。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖吏砂,靈堂內(nèi)的尸體忽然破棺而出撵儿,到底是詐尸還是另有隱情,我是刑警寧澤狐血,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布淀歇,位于F島的核電站,受9級(jí)特大地震影響匈织,放射性物質(zhì)發(fā)生泄漏浪默。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望浴鸿。 院中可真熱鬧,春花似錦弦追、人聲如沸岳链。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)掸哑。三九已至,卻和暖如春零远,著一層夾襖步出監(jiān)牢的瞬間苗分,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工牵辣, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留摔癣,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓纬向,卻偏偏與公主長(zhǎng)得像择浊,于是被迫代替她去往敵國(guó)和親琢岩。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • vue概述 在官方文檔中,有一句話對(duì)Vue的定位說(shuō)的很明確:Vue.js 的核心是一個(gè)允許采用簡(jiǎn)潔的模板語(yǔ)法來(lái)聲明...
    li4065閱讀 7,227評(píng)論 0 25
  • 這篇筆記主要包含 Vue 2 不同于 Vue 1 或者特有的內(nèi)容糕篇,還有我對(duì)于 Vue 1.0 印象不深的內(nèi)容酌心。關(guān)于...
    云之外閱讀 5,050評(píng)論 0 29
  • 本文章是我最近在公司的一場(chǎng)內(nèi)部分享的內(nèi)容谒府。我有個(gè)習(xí)慣就是每次分享都會(huì)先將要分享的內(nèi)容寫成文章。所以這個(gè)文集也是用來(lái)...
    Awey閱讀 9,452評(píng)論 4 67
  • 三五元閱讀 229評(píng)論 1 1
  • 親子日記第五十八篇 今天開(kāi)家長(zhǎng)會(huì)了,孩子爸爸參加的,家長(zhǎng)會(huì)結(jié)束后又接著參加了家委會(huì)的會(huì)議啊鸭,所以回到家已經(jīng)8點(diǎn)20了...
    小丸子兜兜閱讀 176評(píng)論 0 0