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

GIF.gif

前言

最近在做移動端的項目,需要制作移動端的alert彈框和message-box提示信息;之前使用Vue框架的element-ui時,就記得element-ui的彈框,今天深入的研究了一下源碼,然后簡單制作了一點小demo

用到的知識點

Vue組件的定義,Vue的extend

Vue.extend(options)

options參數(shù)是一個對象,一個Vue組件配置項的對象,例如

// 創(chuàng)建一個構(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òu)造器的Vue實例,并且給data重新賦值
const Message = new Profile({
    data: {
        message: 'message'
    }
})
// 將這個實例掛載到 id 為 app 的DOM節(jié)點上
Message.$mount('#app');

正文

看完了基本原理,大部分同學(xué)應(yīng)該就已經(jīng)可以明白這個東西是怎么實現(xiàn)的了吧!,接著我?guī)е蠹襾砜纯淳唧w的實現(xiàn)步驟

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

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

<script>
      // 定義每一個type對應(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值對應(yīng)的icon
                message: '', // 默認(rèn)的message為空,由外部傳入
                duration: 2000 // 默認(rèn)顯示時間為2000ms
            }
        },
        computed: {
                      // 如果外部傳入icon則使用外部的icon,如果沒有。則使用type值對應(yīng)的icon
            iconClass() {
                if(this.icon) {
                    return this.icon;
                }else {
                    return typeClass[this.type];
                }
            }
        }
    }
</script>

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

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

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

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

// 最終拋出一個對象,對象上我們可以使用 install 來擴展Vue的插件
// 當(dāng)我們的對象上有install方法的時候,它接收第一個參數(shù)為Vue,
// 我這里為了方便使用,還在當(dāng)前拋出的對象上定義了一個message方法,為了方便在axios的攔截器中使用;
export default {
    message: Message,
    install(Vue) {
        Vue.prototype.$message = Message;
        Vue.message = Message;
    }
};

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

import Message from './plugins/Message';

Vue.use(Message); // 因為我們的對象上定義了install方法,所以可以直接調(diào)用Vue的use方法

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

我們直接使用this.$message就可以調(diào)用我們的message-box,并且顯示都沒有任何問題,當(dāng)然,也可以通過傳入icon和duration來修改圖標(biāo)和時間

<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,我想你對alert的做法也有了一定的想法了吧!接著我們來看一下alert的做法

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

對于alert來說,我們需要有一個頭部,一個內(nèi)容區(qū),還有一個確認(rèn)按鈕,

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

而我們在這個組件內(nèi),當(dāng)點擊確定按鈕的時候,我們將卸載當(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: {
          // 點擊確定按鈕的時候,首先隱藏當(dāng)前元素,然后將當(dāng)前元素從DOM結(jié)構(gòu)中刪除,并手動卸載當(dāng)前實例
            btnClick() {
                this.visible = false;
                this.$el.parentNode.removeChild(this.$el);
                this.$destroy();
            }
        }
    }
</script>

接著我們再來看定義構(gòu)造器的代碼

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

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

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

// 拋出對象,以及install方法,原理我們在上邊已經(jīng)講過了
export default {
    alert: Alert,
    install(vue) {
        vue.prototype.$alert = Alert;
    }
}

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

import Alert from './plugins/Alert';

Vue.use(Alert);

最后來使用一下這個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é)言

我沒有帖出css樣式文件,因為樣式文件其實沒有多少東西,我覺得你寫的會比我寫的更優(yōu)秀,更完美
看完這篇文章不知道你有沒有什么收獲,如果可以理解的話,希望你通過這篇文章寫一個dialog組件,鞏固一下知識,順手點個關(guān)注
感謝您的查閱,代碼冗余或者有錯誤的地方望不吝賜教;菜鳥一枚,請多關(guān)照
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容