vue3 函数式弹窗
在 Vue 3 中,函数式弹窗是一种常见的需求,尤其是在需要动态创建和销毁组件时(例如消息提示框、确认框等)。Vue 3 提供了强大的 Composition API 和 createVNode
等工具,使得实现函数式弹窗变得非常灵活。
以下是实现函数式弹窗的核心思路和步骤:
1. 核心思路
函数式弹窗的核心是通过编程方式动态挂载一个组件到 DOM 中,而不需要手动在模板中声明。这通常包括以下步骤:
- 定义弹窗组件:创建一个独立的弹窗组件。
- 动态挂载组件:使用 Vue 的
createVNode
和render
方法将组件挂载到指定的 DOM 节点。 - 提供关闭功能:在弹窗组件中提供关闭逻辑,并通过回调或 Promise 返回用户操作结果。
- 清理挂载节点:在弹窗关闭后,移除组件实例以避免内存泄漏。
2. 实现步骤
(1) 定义弹窗组件
首先,创建一个简单的弹窗组件,例如 Modal.vue
:
<template>
<div class="modal-overlay" @click.self="close">
<div class="modal-content">
<h3>{{ title }}</h3>
<p>{{ content }}</p>
<button @click="confirm">确认</button>
<button @click="close">取消</button>
</div>
</div>
</template>
<script>
import { ref } from 'vue';
export default {
props: {
title: String,
content: String,
},
emits: ['close', 'confirm'],
setup(props, { emit }) {
const close = () => {
emit('close');
};
const confirm = () => {
emit('confirm');
close();
};
return { close, confirm };
},
};
</script>
<style scoped>
.modal-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
}
.modal-content {
background: white;
padding: 20px;
border-radius: 8px;
text-align: center;
}
</style>
(2) 创建函数式调用方法
接下来,编写一个函数来动态挂载和卸载弹窗组件:
import { createVNode, render } from 'vue';
import Modal from './Modal.vue';
// 动态创建弹窗的函数
function createModal(options) {
return new Promise((resolve, reject) => {
// 创建一个容器元素
const container = document.createElement('div');
document.body.appendChild(container);
// 创建 VNode
const vnode = createVNode(Modal, {
...options,
onClose: () => {
// 清理组件
render(null, container);
document.body.removeChild(container);
resolve(false); // 用户取消
},
onConfirm: () => {
// 清理组件
render(null, container);
document.body.removeChild(container);
resolve(true); // 用户确认
},
});
// 挂载组件
render(vnode, container);
});
}
export default createModal;
(3) 使用函数式弹窗
现在可以在任何地方调用 createModal
函数来显示弹窗:
import createModal from './createModal';
async function showCustomModal() {
const result = await createModal({
title: '提示',
content: '确定要执行此操作吗?',
});
if (result) {
console.log('用户点击了确认');
} else {
console.log('用户点击了取消');
}
}
// 调用示例
showCustomModal();
3. 实现细节解析
(1) 动态挂载组件
createVNode
:用于创建虚拟 DOM 节点。render
:将虚拟 DOM 渲染到指定的 DOM 容器中。- 在组件销毁时,调用
render(null, container)
清空挂载点的内容。
(2) 异步控制
- 使用
Promise
包装弹窗逻辑,便于在用户操作完成后返回结果。 resolve
和reject
分别对应用户确认和取消的操作。
(3) 避免内存泄漏
- 在弹窗关闭后,确保从 DOM 中移除挂载的容器,避免残留节点导致内存泄漏。
4. 可选优化
(1) 全局注册
可以将 createModal
方法挂载到 Vue 的全局属性上,方便在任何组件中调用:
import { createApp } from 'vue';
import App from './App.vue';
import createModal from './createModal';
const app = createApp(App);
// 注册为全局方法
app.config.globalProperties.$modal = createModal;
app.mount('#app');
然后在组件中使用:
this.$modal({ title: '提示', content: '这是一个全局弹窗' }).then((result) => {
console.log(result ? '确认' : '取消');
});
(2) 插件化
将弹窗逻辑封装为一个插件,方便复用:
export default {
install(app) {
app.config.globalProperties.$modal = createModal;
},
};
在主文件中引入插件:
import ModalPlugin from './plugins/modal';
app.use(ModalPlugin);
5. 总结
通过 Vue 3 的 createVNode
和 render
方法,我们可以轻松实现函数式弹窗。这种方式不仅灵活,还能很好地与 Composition API 结合,满足动态组件挂载的需求[[1]].