寫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