Vue利用Vue.extend()实现自定义弹出框
程序员文章站
2024-03-04 16:50:23
...
container层html(遮罩层)
<template>
<transition name="dialog">
<div class="container" v-show="visible" @click="maskClose">
<div class="content" @click.stop="stopEvent"></div>
</div>
</transition>
</template>
container层js
export default {
name: 'self-container',
props: {
maskCloseDialog: { // 是否支持遮罩层点击后关闭
type: Boolean,
default: false
},
closeCallback: { // 关闭弹出框后是否有回调,有就要调用
type: Function,
default: null
}
},
data() {
return {
visible: false
}
},
methods: {
stopEvent() {
},
maskClose() {
if (this.maskCloseDialog) {
this.close()
}
}
}
}
container层css
.container {
height: 100%;
width: 100%;
position: fixed;
top: 0;
left: 0;
background: rgba(0, 0, 0, 0.5);
z-index: 99999;
}
.content {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.dialog-enter, .dialog-leave-to {
opacity: 0;
}
.dialog-enter-active, .dialog-leave-active {
transition: opacity .5s;
}
弹出框内容的组件html
<template>
<div>
<h1 @click="close">qqq</h1>
<h2 @click="test">aaa</h2>
<div>{{name}}</div>
</div>
</template>
弹出框内容组件js
export default {
name: 'test',
props: {
name: {
type: String,
default: ''
}
},
methods: {
close() {
this.$close() // 这个是在最后一个js中去扩展的
},
test() {
console.log('test')
}
}
}
启用弹出框的组件html
<template>
<div class="map-container">
<div class="info">
<button @click="test">测试一下弹出框</button>
<div>{{state}}</div>
</div>
</div>
</template>
启用弹出框的组件js
import Test from './test.vue' // 自定义的弹出框内容组件
export default {
name: 'target',
data: () => {
return {
state: ''
}
},
methods: {
test() {
this.state = '弹框已打开'
this.dom = this.$asynDialog({
component: Test,
data: {
name: 'rose'
},
config: {
closeCallback: () => {
this.state = '弹框已关闭'
console.log('close')
}
}
})
}
}
}
核心js,利用vue.extend将container将自定义组件包装起来,动态添加到body中,并显示出来
import Vue from 'vue'
import container from './container.vue'
const containerConstructor = Vue.extend(container)
let cache = [] // 缓存container的实例
let getComponentInstance = (constructor) => {
if (cache.length > 0) {
let instance = cache[0]
cache.splice(0, 1)
return instance
}
return new constructor({
el: document.createElement('div')
})
}
let cacheInstance = (instance) => {
if (instance) {
cache.push(instance)
}
}
let removeContainerDom = (e) => {
// 先移除自定义组件的元素,再移除外层包装的组件元素
const content = e.target.querySelector('.content')
content.removeChild(content.childNodes[0])
if (e.target.parentNode) {
e.target.parentNode.removeChild(e.target);
}
}
containerConstructor.prototype.close = function() {
this.visible = false;
this.$el.addEventListener('transitionend', removeContainerDom);
this.closed = true;
cacheInstance(this)
if (this.closeCallback) {
this.closeCallback()
}
}
function showDialog(object) {
const containerInstance = getComponentInstance(containerConstructor)
const { component, data, config } = object
const dialogContructor = Vue.extend(component)
dialogContructor.prototype.$close = containerConstructor.prototype.close.bind(containerInstance)
const dialogBodyInstance = new dialogContructor({
el: containerInstance.$el
})
// 将需要传给自定义组件的数据传入
for(let i in data) {
dialogBodyInstance[i] = data[i]
}
containerInstance.closed = false
containerInstance.duration = config ? config.duration : 0
containerInstance.maskCloseDialog = config ? config.maskCloseDialog : false
containerInstance.closeCallback = config ? config.closeCallback : null
clearTimeout(containerInstance.timer);
containerInstance.$el.querySelector('.content').appendChild(dialogBodyInstance.$el)
document.body.appendChild(containerInstance.$el)
// 元素在body中添加好后
Vue.nextTick(() => {
containerInstance.visible = true
containerInstance.$el.removeEventListener('transitionend', removeContainerDom)
const { duration } = containerInstance
if (duration && duration > 0) {
containerInstance.timer = setTimeout(() => {
if (containerInstance.closed) {
return
}
containerInstance.close()
}, duration)
}
})
return containerInstance
}
function asynDialog() {
Vue.prototype.$asynDialog = showDialog
}
export default asynDialog
最后在vue代码框架的入口文件main.js中书写
import asynDialog from '@/utils/asyn-dialog/asny-dialog-component.js'
Vue.use(asynDialog) // Vue.use发现传入的参数为function时,就会调用这个function