如何优雅的写一个Vue 的弹框

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

回到顶部