Vue實(shí)現(xiàn)移動(dòng)端 message-box 與 alert 彈框@郝晨光

GIF.gif

前言

最近在做移動(dòng)端的項(xiàng)目镰吵,需要制作移動(dòng)端的alert彈框和message-box提示信息;之前使用Vue框架的element-ui時(shí)壶硅,就記得element-ui的彈框梨树,今天深入的研究了一下源碼,然后簡(jiǎn)單制作了一點(diǎn)小demo

用到的知識(shí)點(diǎn)

Vue組件的定義涨共,Vue的extend

Vue.extend(options)

options參數(shù)是一個(gè)對(duì)象纽帖,一個(gè)Vue組件配置項(xiàng)的對(duì)象,例如

// 創(chuàng)建一個(gè)構(gòu)造器举反,
const Profile = Vue.extend({
// 模板使用參數(shù)內(nèi)的template模板
  template: `<div>{{message}}</div>`,
// data內(nèi)的為默認(rèn)數(shù)據(jù)
  data() {
      return {
          message: ''  
      }
  }
})
// 創(chuàng)建一個(gè)基于構(gòu)造器的Vue實(shí)例抛计,并且給data重新賦值
const Message = new Profile({
    data: {
        message: 'message'
    }
})
// 將這個(gè)實(shí)例掛載到 id 為 app 的DOM節(jié)點(diǎn)上
Message.$mount('#app');

正文

看完了基本原理,大部分同學(xué)應(yīng)該就已經(jīng)可以明白這個(gè)東西是怎么實(shí)現(xiàn)的了吧照筑!吹截,接著我?guī)е蠹襾?lái)看看具體的實(shí)現(xiàn)步驟

首先需要先定義一個(gè)Vue的組件瘦陈,這個(gè)組件不直接掛載到任何DOM節(jié)點(diǎn)中,而是作為一個(gè)模板波俄,一個(gè)構(gòu)造器來(lái)使用

<template>
    <transition name="fade">
            <!-- 使用fade淡入淡出動(dòng)畫晨逝,使用type變量來(lái)控制class類名,達(dá)到更改type值就可以修改樣式的效果 -->
        <div :class="['plugins-message-box',type]" v-show="visible">
            <!-- 使用iconClass來(lái)控制icon的類名懦铺,我使用的是阿里的字體圖標(biāo)庫(kù)iconfont捉貌,可以根據(jù)個(gè)人愛(ài)好來(lái)更換 -->
            <div :class="['message-icon','iconfont',iconClass]"></div>
            <!-- 輸出消息 -->
            <div class="message-container">{{message}}</div>
        </div>
    </transition>
</template>

<script>
      // 定義每一個(gè)type對(duì)應(yīng)的class類名
    const typeClass = {
        success: 'icon-success',
        error: 'icon-error',
        default: 'icon-success'
    };
    export default {
        name: "messageMain",
            // 定義的是默認(rèn)數(shù)據(jù),默認(rèn)值
        data() {
            return {
                visible: false, // 控制DOM顯示隱藏
                type: 'default', // 默認(rèn)type值為default
                icon: '', // 默認(rèn)使用icon為空冬念,則使用type值對(duì)應(yīng)的icon
                message: '', // 默認(rèn)的message為空趁窃,由外部傳入
                duration: 2000 // 默認(rèn)顯示時(shí)間為2000ms
            }
        },
        computed: {
                      // 如果外部傳入icon則使用外部的icon,如果沒(méi)有急前。則使用type值對(duì)應(yīng)的icon
            iconClass() {
                if(this.icon) {
                    return this.icon;
                }else {
                    return typeClass[this.type];
                }
            }
        }
    }
</script>

可以看到這是一個(gè)非常簡(jiǎn)單的Vue組件模板醒陆,使用的是vue-cli工具構(gòu)建的,最終它拋出了一個(gè)對(duì)象裆针,而我們接著就應(yīng)該來(lái)定義構(gòu)造器

import Vue from 'vue'; // 引入Vue
import MessageMain from './messageMain'; // 引入上邊定義好的message模板

const MessageBox = Vue.extend(MessageMain); // 使用Vue.extend來(lái)創(chuàng)建一個(gè)構(gòu)造器
let instance; // instance 變量用來(lái)保存實(shí)例
let timer = null; // timer 變量用來(lái)保存定時(shí)器

// 定義一個(gè)function刨摩,參數(shù)為options,默認(rèn)為一個(gè)對(duì)象
const Message = function(options = {}) {
    // 如果當(dāng)前處在服務(wù)器端世吨,則直接返回
    if(Vue.prototype.$isServer) return;
    // 如果當(dāng)前定時(shí)器已開啟澡刹,說(shuō)明頁(yè)面上已經(jīng)有一個(gè)message-box了,則不能再繼續(xù)創(chuàng)建新的message-box
    if(timer) return;
    // 對(duì)options做處理耘婚,如果直接傳入string罢浇,則使其保存在options的message屬性上
    if(typeof options === 'string') {
        options = {
            message: options
        }
    }
    // 初始化實(shí)例,并將options作為新的data傳入沐祷,Vue會(huì)將options合并到原有的data上己莺,覆蓋原有的默認(rèn)值,但是戈轿,在options中沒(méi)有設(shè)置的是不會(huì)被改變的
    instance = new MessageBox({
        data: options
    });
    // 調(diào)用$mount方法凌受,將當(dāng)前實(shí)例渲染為真實(shí)DOM,生成$el思杯,胜蛉,如果不執(zhí)行這一步,將拿不到 $el 的值色乾,但是不指定DOM節(jié)點(diǎn)接管當(dāng)前實(shí)例
    instance.vm = instance.$mount();
    // 使用原生js的API將當(dāng)前實(shí)例的真實(shí)DOM追加到body中
    document.body.appendChild(instance.vm.$el);
    // 實(shí)例上的vm就是我們的Vue組件誊册,所以我們可以通過(guò)vm訪問(wèn)到當(dāng)前實(shí)例中的所有屬性
    // 將visible設(shè)置為true,即顯示當(dāng)前message-box
    instance.vm.visible = true;
    // 開啟定時(shí)器
    timer = setTimeout(() => {
       // 在時(shí)間結(jié)束后將當(dāng)前實(shí)例手動(dòng)卸載
        instance.vm.$destroy();
       // 使用原生API將當(dāng)前實(shí)例生成的DOM節(jié)點(diǎn)在真實(shí)的DOM樹中刪除
        instance.vm.$el.parentNode.removeChild(instance.vm.$el);
       // 清除定時(shí)器
        timer = null;
    }, instance.vm.duration);
    // 定時(shí)器的時(shí)間使用vm中定義的時(shí)間
    return instance.vm;
};

// 最終拋出一個(gè)對(duì)象暖璧,對(duì)象上我們可以使用 install 來(lái)擴(kuò)展Vue的插件
// 當(dāng)我們的對(duì)象上有install方法的時(shí)候案怯,它接收第一個(gè)參數(shù)為Vue,
// 我這里為了方便使用澎办,還在當(dāng)前拋出的對(duì)象上定義了一個(gè)message方法嘲碱,為了方便在axios的攔截器中使用金砍;
export default {
    message: Message,
    install(Vue) {
        Vue.prototype.$message = Message;
        Vue.message = Message;
    }
};

接著來(lái)看一下使用方法,首先要在main.js中將這個(gè)插件安裝到Vue上

import Message from './plugins/Message';

Vue.use(Message); // 因?yàn)槲覀兊膶?duì)象上定義了install方法,所以可以直接調(diào)用Vue的use方法

安裝完成之后麦锯,我們就可以在任意組件內(nèi)使用了恕稠,這個(gè)是我們剛剛在文章開頭看到的頁(yè)面,現(xiàn)在只有調(diào)用message-box的方法扶欣;

我們直接使用this.$message就可以調(diào)用我們的message-box鹅巍,并且顯示都沒(méi)有任何問(wèn)題,當(dāng)然料祠,也可以通過(guò)傳入icon和duration來(lái)修改圖標(biāo)和時(shí)間

<template>
    <div class="test">
        <div class="test-message">
            <button @click="messageClick('default')">
                default
            </button>
            <button @click="messageClick('success')">
                success
            </button>
            <button @click="messageClick('error')">
                error
            </button>
        </div>
    </div>
</template>

<script>
    export default {
        name: "test",
        methods: {
            messageClick(type) {
                this.$message({
                    type,
                    message: type
                })
            }
        }
    }
</script>
GIF.gif

看完了message-box骆捧,我想你對(duì)alert的做法也有了一定的想法了吧!接著我們來(lái)看一下alert的做法

在alert中髓绽,我們不需要定義type敛苇,也不需要修改樣式,

對(duì)于alert來(lái)說(shuō)梧宫,我們需要有一個(gè)頭部,一個(gè)內(nèi)容區(qū)摆碉,還有一個(gè)確認(rèn)按鈕塘匣,

而我們的內(nèi)容區(qū),使用的是v-html來(lái)渲染內(nèi)容巷帝,便于我們可以使用原生的DOM標(biāo)簽來(lái)實(shí)現(xiàn)一些簡(jiǎn)單的樣式

而我們?cè)谶@個(gè)組件內(nèi)忌卤,當(dāng)點(diǎn)擊確定按鈕的時(shí)候,我們將卸載當(dāng)前組件楞泼,并將當(dāng)前組件從DOM結(jié)構(gòu)刪除

<template>
    <transition name="fade">
        <div class="plugins-alert-box" v-show="visible">
            <div class="plugins-alert-container">
                <div class="alert-box-title">
                    {{title}}
                </div>
                <div class="alert-box-message" v-html="message">
                </div>
                <div class="alert-box-btn">
                    <button @click="btnClick">確定</button>
                </div>
            </div>
        </div>
    </transition>
</template>

<script>
    export default {
        name: "alertMain",
        data() {
            return {
                visible: false,
                message: '',
                title: '提示'
            }
        },
        methods: {
          // 點(diǎn)擊確定按鈕的時(shí)候驰徊,首先隱藏當(dāng)前元素,然后將當(dāng)前元素從DOM結(jié)構(gòu)中刪除堕阔,并手動(dòng)卸載當(dāng)前實(shí)例
            btnClick() {
                this.visible = false;
                this.$el.parentNode.removeChild(this.$el);
                this.$destroy();
            }
        }
    }
</script>

接著我們?cè)賮?lái)看定義構(gòu)造器的代碼

import Vue from 'vue'; // 引入Vue
import alertMain from './alertMain'; // 引入定義好的alert的模板

const AlertBox = Vue.extend(alertMain); // 創(chuàng)建一個(gè)構(gòu)造器
let instance; // instance 變量用來(lái)保存實(shí)例

// 定義 Alert函數(shù)棍厂,接收options參數(shù),默認(rèn)為一個(gè)對(duì)象
const Alert = function(options = {}) {
    if(Vue.prototype.$isServer) return; // 判斷當(dāng)前如果是在服務(wù)器端渲染的話超陆,直接返回
    // 同樣的牺弹,我們還要處理options
    if(typeof options === 'string') {
        options = {
            message: options
        }
    }
    // 創(chuàng)建一個(gè)alert-box實(shí)例 ,將options作為新的data傳入
    instance = new AlertBox({
        data: options
    });
    // 調(diào)用$mount方法时呀,創(chuàng)建真實(shí)DOM张漂,獲取到vm
    instance.vm = instance.$mount();
    // 調(diào)用原生API將當(dāng)前實(shí)例的真實(shí)DOM追加到body中
    document.body.appendChild(instance.vm.$el);
    // 讓當(dāng)前DOM顯示
    instance.vm.visible = true;
    return instance.vm;
};

// 拋出對(duì)象,以及install方法谨娜,原理我們?cè)谏线呉呀?jīng)講過(guò)了
export default {
    alert: Alert,
    install(vue) {
        vue.prototype.$alert = Alert;
    }
}

接著就是alert-box的使用方法了航攒,同樣是需要先use一下

import Alert from './plugins/Alert';

Vue.use(Alert);

最后來(lái)使用一下這個(gè)alert-box

<template>
    <div class="test">
        <div class="test-message">
            <button @click="alertClick">alert</button>
        </div>
    </div>
</template>

<script>
    export default {
        name: "test",
        methods: {
            alertClick() {
                this.$alert({
                    message: `<b>加粗文字</b>`
                })
            }
        }
    }
</script>
GIF.gif

結(jié)言

我沒(méi)有帖出css樣式文件,因?yàn)闃邮轿募鋵?shí)沒(méi)有多少東西趴梢,我覺(jué)得你寫的會(huì)比我寫的更優(yōu)秀漠畜,更完美
看完這篇文章不知道你有沒(méi)有什么收獲币他,如果可以理解的話,希望你通過(guò)這篇文章寫一個(gè)dialog組件盆驹,鞏固一下知識(shí)圆丹,順手點(diǎn)個(gè)關(guān)注
感謝您的查閱,代碼冗余或者有錯(cuò)誤的地方望不吝賜教躯喇;菜鳥一枚辫封,請(qǐng)多關(guān)照
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市廉丽,隨后出現(xiàn)的幾起案子倦微,更是在濱河造成了極大的恐慌,老刑警劉巖正压,帶你破解...
    沈念sama閱讀 218,607評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件欣福,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡焦履,警方通過(guò)查閱死者的電腦和手機(jī)拓劝,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,239評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)嘉裤,“玉大人郑临,你說(shuō)我怎么就攤上這事⌒汲瑁” “怎么了厢洞?”我有些...
    開封第一講書人閱讀 164,960評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)典奉。 經(jīng)常有香客問(wèn)我躺翻,道長(zhǎng),這世上最難降的妖魔是什么卫玖? 我笑而不...
    開封第一講書人閱讀 58,750評(píng)論 1 294
  • 正文 為了忘掉前任公你,我火速辦了婚禮,結(jié)果婚禮上假瞬,老公的妹妹穿的比我還像新娘省店。我一直安慰自己,他們只是感情好笨触,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,764評(píng)論 6 392
  • 文/花漫 我一把揭開白布懦傍。 她就那樣靜靜地躺著,像睡著了一般芦劣。 火紅的嫁衣襯著肌膚如雪粗俱。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,604評(píng)論 1 305
  • 那天虚吟,我揣著相機(jī)與錄音寸认,去河邊找鬼签财。 笑死,一個(gè)胖子當(dāng)著我的面吹牛偏塞,可吹牛的內(nèi)容都是我干的唱蒸。 我是一名探鬼主播,決...
    沈念sama閱讀 40,347評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼灸叼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼神汹!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起古今,我...
    開封第一講書人閱讀 39,253評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤屁魏,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后捉腥,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體氓拼,經(jīng)...
    沈念sama閱讀 45,702評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,893評(píng)論 3 336
  • 正文 我和宋清朗相戀三年抵碟,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了桃漾。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,015評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡拟逮,死狀恐怖撬统,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情唱歧,我是刑警寧澤宪摧,帶...
    沈念sama閱讀 35,734評(píng)論 5 346
  • 正文 年R本政府宣布粒竖,位于F島的核電站颅崩,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏蕊苗。R本人自食惡果不足惜沿后,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,352評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望朽砰。 院中可真熱鬧尖滚,春花似錦、人聲如沸瞧柔。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,934評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)造锅。三九已至撼唾,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間哥蔚,已是汗流浹背倒谷。 一陣腳步聲響...
    開封第一講書人閱讀 33,052評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工蛛蒙, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人渤愁。 一個(gè)月前我還...
    沈念sama閱讀 48,216評(píng)論 3 371
  • 正文 我出身青樓牵祟,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親抖格。 傳聞我的和親對(duì)象是個(gè)殘疾皇子诺苹,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,969評(píng)論 2 355