如何優雅的寫一個Vue 的彈框

寫Vue或者是react 都會碰見彈框的問題。也嘗試了多種辦法來寫彈框,一直都不太滿意,今天特意看了一下 Element UI 的源碼,模仿着寫了一個簡易版。javascript

大概有一下幾個問題:vue

一、彈框的層級問題,若是在嵌套的組件裏面使用了彈框,可能會出現彈框的層級不夠高java

二、彈框的函數調用方式react

首先第一點:彈框的層級promise

若是將彈框放置在最外層,body下面。就不會有層級問題。緩存

第二點:彈框的函數調用app

首先咱們能夠思考,將組件的實例拿到,然而初學的時候好像只有 經過  refs 能拿到組件的對象,而後調用顯示隱藏函數

其實咱們能夠經過 VUE.extend  這個函數,對組件進行初始化,而後能夠拿到 組件對象學習

對於 Vue.extend 不太清楚的,建議本身去百度學習一下。this

 

下面給出彈框的代碼: alert.vue 文件下面

<template>
    <div class="_alert" v-show="visible">
        <div class="wind-alert">
	        <div class="wind-alert-bg"></div>
	        <div class="wind-alert-dialog animate-scale">
	          <div class="wind-alert-title">{{title}}</div>
	          <div class="wind-alert-content">{{content}}</div>
	          <div class="wind-alert-btn" @click="close">{{btn}}</div>
	        </div>
	    </div>
    </div>
</template>
<script>
export default {
    name:"rule_alert",
     data() {
	    return {
            title: '提示',
            content:  '',
            btn:  '肯定',
            visible:false
        }
    },
    methods: {
        close() {
            this.visible = false;
           this._promise &&  this._promise.resolve()
        }
    },
    watch: {
        '$route' () {
            this.close();
        }
    }
}
</script>
<style>
.wind-alert-dialog {
    top: 30%;
    width: 80%;
    left: 50%;
    opacity: 1;
    position: fixed;
    margin-left: -40%;
    font-size: 14px;
    text-align: center;
    font-family: 'Microsoft Yahei';
    background: #FFFFFF;
    border-radius: 8px;
    z-index: 999999999;
    box-sizing: content-box;
}

.wind-alert-bg {
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    opacity: 0.3;
    display: block;
    position: fixed;
    z-index: 999999998;
    background-color: #000000;
}

.wind-alert-title {
    font-size: 17px;
    padding: 20px 5px 0;
}

.wind-alert-content {
    padding: 5px 15px 20px 15px;
    border-bottom: 1px solid #ededed;
}

.wind-alert-btn {
    color: #0582cd;
    font-size: 15px;
    line-height: 40px;
    font-weight: bold;
}

.animate-scale {
    animation-name: scale;
    animation-duration: 0.375s;
}

@keyframes scale {
    0%{
        transform: scale(0);
    }
    100% {
        transform: scale(1);
    }
}
</style>

  

接下來,就是將這個組件,進行初始化,而且注入一些本身的方法和屬性

這個地方的注入,是一個公共的方法,後面能夠引入其餘類型的彈框,好比 comfirm 類型的

新建  plugin.js

import Alert from "@/components/alert";

import Vue from "vue";

//原始組件
var components = {
    Alert:Alert
}

var instance = {}; //緩存組件的實例
var Ruler = {};  //組件的集合
var body  =  document.body || document.documentElement;
var root = document.createElement("div");
body.appendChild(root);

//初始化構造vue組件,而且注入本身的代碼
const initComponents = function(type,options){
    options  =  options || {};
    type = type || '';
    if(components[type]){   
        if(!instance[type]){
            //避免重複的初始化
            var div = document.createElement('div');
            root.appendChild(div);
            const MessageBoxConstructor = Vue.extend(components[type]);
              
            instance[type]  = new MessageBoxConstructor({
                el: div
            });
        }
        var ins = instance[type];
        //複製屬性
        for(var i in options){
            ins[i] = options[i];
        }
        Vue.nextTick(()=>{
            ins.visible = true;
        })
        return new Promise(function(resolve,reject){
            //注入當前的 promise
            ins._promise = {
                resolve,
                reject
            };
        }).finally(()=>{
            //ins.visible = false;
            //能夠在這裏監聽,無論結果如何,最後執行一段代碼
        });
    }else{
        return Promise.reject("組件不存在");    
    };
}
//開始註冊組件

var Ruler = {};  //組件的集合

//主動關閉某個組件彈窗  type 組件類型名稱, methods  false  取消關閉, true 確認關閉
Ruler.closeComponents = function (type,methods) {
    if(instance[type] && instance[type]._promise){
        if(methods){
          instance[type]._promise.resolve();
        }else{
          instance[type]._promise.reject();
        }
      instance[type].visible = false;
    }
}


//對彈出組件的初始化處理
function popupHandle(i,options){
  if(typeof options == "string"){
    options  = {
      msg: options
    }
  }
  return  initComponents(i,options);
}


for(var i in components){
  Ruler[i] = popupHandle.bind(components[i],i);
}
export default{
    install(Vue){
       Vue.prototype.$Ruler = Ruler;
    }
}

  

 

接下來就是在 main.js 裏面引入了:

import plugin from "@/plugin/plugin";
Vue.use(plugin);

  

而後在任意的地方使用

this.$Ruler.Alert("這是一個提示").then((ret)=>{
          console.log("then",ret);
      }).catch((e)=>{
          console.log("catch",e);
      });

  

 注意: Vue.extend 初始化的組件,其內部  this.$router 是 undefined ,還有 this.$store 也是,能夠直接import 後使用,

也能夠  在組件裏面 導入 router 以後,和data 平級,寫一個router,,,,這樣,組件在傳入 Vue.extend的時候,就可以訪問到 this.$router 了

 

或者在 Vue.extend 之後 new 的時候加入 router ,如:

instance[type]  = new MessageBoxConstructor({
    el: div,
	router:router
});

  

其原理在於: Vue.extend 函數,返回的一個繼承了 Vue 的子類。

也就是說 

new MessageBoxConstructor  的時候,等同於  new Vue
相關文章
相關標籤/搜索