我们会遇到很多类似于这样的场景,公共组件弹窗,点击确定/取消按钮会执行某个函数。一般做法是同props把弹窗一些value,function注入进去,然后子组件emit执行一下,但是这样有一个问题,就是太麻烦了。本文通过动态挂载组件的 方式去解决这个需求,废话少说,直接上代码。
项目结构:
![在这里插入图片描述](https://img-blog.csdnimg.cn/3503a61ff35e479893119d37b9cca731.png)
首先,我们先封装一下动态挂载方法:
mountComponent.ts
import { App, Component, createApp } from 'vue'
function isInDocument(element: HTMLElement) {
return document.body.contains(element)
}
export function mountComponent(RootComponent: Component, className: string, singleton?: boolean) {
const root = document.createElement('div')
root.className = (className || '') + '-wrap'
const app: App<Element> = createApp(RootComponent)
const instance: any = app.mount(root)
if (singleton) {
if (!isInDocument(root)) {
document.body.appendChild(root)
} else {
document.body.appendChild(root)
}
}
return {
instance,
unmount() {
try {
app.unmount()
} catch(err) {
console.log(err)
}
}
}
}
然后,我们定义一个公共组件:
modal/index.vue
注意:当时我尝试用setup那种写法时,我发现数据注入不进去,因为时间关系,没去找原因,如果大佬知道的话,记得告诉下我。必须return这些定义好的value和function,否则数据无法注入。
<template>
<div v-show="show" class="modal">
<div v-show="title">{{ title }}</div>
<div v-show="message">{{ message }}</div>
<div>
<button v-show="showConfirmBtn" @click="confirm">确认</button>
<button v-show="showCancelBtn" @click="cancel">取消</button>
</div>
</div>
</template>
<script lang="ts" >
import { ref, defineComponent } from 'vue'
export default defineComponent({
name: 'modal',
setup() {
const show = ref(false)
const message = ref('')
const title = ref('')
const showConfirmBtn = ref(true)
const showCancelBtn = ref(true)
const callback = ref<(action: string) => void>(() => {})
const close = (action: string) => {
setTimeout(() => {
//show.value = false
console.log('执行回调哈桑农户')
callback.value(action)
}, 500);
}
const confirm = () => {
close('confirm')
}
const cancel = () => {
close('cancel')
}
return {
show,
message,
title,
callback,
close,
showConfirmBtn,
showCancelBtn,
confirm,
cancel
}
}
})
</script>
<style>
.modal {
position: fixed;
}
</style>
最关键的代码来了
modal/index.ts
import { mountComponent } from '@/util/mountComponent'
import modal from './index.vue'
interface ModalInstance {
instance: any
unmount: () => void
}
let modalInstance: ModalInstance
export interface ModalOptions {
show?: boolean
title?: string
message?: string
showConfirmBtn?: boolean
showCancelBtn?: boolean
callback?: (action: string) => void
}
function createInstance() {
if (modalInstance) {
return modalInstance
}
modalInstance = mountComponent(modal, 'modal', true)
return modalInstance
}
Modal.defaultOptions = {
show: false,
title: '',
message: '',
showConfirmBtn: true,
showCancelBtn: true,
callback: (action: string) => {
modalInstance?.instance[action === 'confirm' ? 'resolve' : 'reject'](action)
}
}
function Modal(options: ModalOptions) {
return new Promise((resolve, reject) => {
const { instance } = createInstance()
Object.assign(instance, Modal.defaultOptions, typeof options === 'string' ? { message: options } : options, {
resolve,
reject
})
instance.show = true
})
}
Modal.confirm = (options: ModalOptions) => {
Modal({
...options,
showConfirmBtn: true
})
}
export default Modal
使用
使用也非常简单:
![在这里插入图片描述](https://img-blog.csdnimg.cn/9869242332154dc9889fa41b0c8330fb.png)
![在这里插入图片描述](https://img-blog.csdnimg.cn/2c3d846409314bd9a2be0fa9d462d9cf.png)
原理这些下次再解释。。。