如何优雅的写一个Vue 的弹框
写Vue或者是react 都会遇见弹框的问题。也尝试了多种办法来写弹框,一直都不太满意,今天特地看了一下 Element UI 的源码,模仿着写了一个简易版。
大概有一下几个问题:
1、弹框的层级问题,如果在嵌套的组件里面使用了弹框,可能会出现弹框的层级不够高
2、弹框的函数调用方式
首先第一点:弹框的层级
如果将弹框放置在最外层,body下面。就不会有层级问题。
第二点:弹框的函数调用
首先我们可以思考,将组件的实例拿到,然而初学的时候好像只有 通过 refs 能拿到组件的对象,然后调用显示隐藏
其实我们可以通过 VUE.extend 这个函数,对组件进行初始化,然后可以拿到 组件对象
对于 Vue.extend 不太清楚的,建议自己去百度学习一下。
下面给出弹框的代码: 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
二、灵活传组件调用,在用 react 的时候,是因为 jsx 天然支持props 可以传组件进去。但是vue似乎不太方便
这里写一个容器,可以传自己想要的组件。调用如下:
下面是实现 Diag 的代码:
Diag.vue
<template><div class="daig-vue" v-if="visible">
<div class="header" v-if="showHeader">
<my-footer v-bind="propsHeader" v-on="onHeader" />
</div>
<div class="content" v-if="showContent">
<my-content v-bind="propsContent" v-on="onContent" />
</div>
<div class="header" v-if="showFooter">
<my-footer v-bind="propsFooter" v-on="onFooter" />
</div>
<button @click="close" >关闭</button>
</div>
</template>
<script>
export default {
data(){
return {
propsHeader:null,
onHeader:null,
showHeader:false,
propsContent:null,
onContent:null,
showContent:false,
propsFooter:null,
onFooter:null,
showFooter:false,
visible:false,
}
},
methods:{
close(){
this.$emit("closed" , "666666")
},
open(params = {}){
if(params.footer){
this.$options.components["my-footer"] = params.footer;
this.propsFooter = params.propsFooter;
this.showFooter = true;
}else{
this.$options.components["my-footer"] = null;
this.showFooter = false;
}
if(params.content){
this.$options.components["my-content"] = params.content;
this.showContent = true;
}else{
this.$options.components["my-content"] = null;
this.showContent = false;
}
if(params.header){
this.$options.components["my-header"] = params.header;
this.showHeader = true;
}else{
this.$options.components["my-header"] = null;
this.showHeader = false;
}
this.visible = true;
}
}
}
</script>
<style lang="scss" scoped>
.daig-vue{
position: absolute;
left: 0;
top:0;
height:100vh;
width:100vw;
z-index: 2000;
background: rgba(0,0,0,0.6);
}
</style>
index.js
import Vue from 'vue'import Diag from "./Diag.vue";
const body = document.body;
const root = document.createElement("div");
body.appendChild(root);
let cache = [];
function getDiag (){
if(cache.length > 0 ){
return cache.pop();
}
let newVue = Vue.extend(Diag);
var div = document.createElement('div');
root.appendChild(div);
let vm = new newVue({
el:div,
// router:router,
});
return vm;
}
export default function (params){
let vm = getDiag();
vm.open(params);
return new Promise((resolve, reject) =>{
vm.$once("closed",(e)=>{
resolve(e);
vm.visible = false;
cache.push(vm);
});
});
}
以上是 如何优雅的写一个Vue 的弹框 的全部内容, 来源链接: utcz.com/z/379508.html