前言
这是一个基于vue3的命令式调用弹窗组件的封装。
只要是使用主流的UI组件库(ant-design/element-ui)的弹窗组件或者自己封装的弹窗组件(基于状态驱动弹窗显示/隐藏)都可以支持。
示例代码
需求:页面有一个名称字段,通过弹窗可以编辑这个字段
页面代码
<template>
<div class="text-center mt-[100px]">
<div>{{ name }}</div>
<a-button @click="showInputDialog">编辑名称</a-button>
</div>
</template>
<script setup lang="tsx">
import { showDialog } from "dialog-async";
import InputDialog from "./InputDialog.vue";
const name = ref("测试");
const showInputDialo = async () => {
// 使用jsx
name.value = await showDialog(<InputDialog defaultValue={name.value} />);
// 使用h函数
name.value = await showDialog(h(InputDialog, {defaultValue: name.value}));
};
</script>
弹窗组件代码 InputDialog.vue
<template>
<a-modal
title="弹窗"
:visible="dialog.visible"
@cancel="dialog.cancel"
@ok="dialog.submit(text)"
>
<a-input v-model:value="text" placeholder="请输入文本" />
</a-modal>
</template>
<script setup lang="ts">
import { useDialog } from "dialog-async";
const props = defineProps({
defaultValue: String,
});
const text = ref(props.defaultValue);
const dialog = useDialog();
</script>
showDialog()
和useDialog()
都会返回这个Promise对象- 将弹窗控制抽象为一个拥有
visible
、cancel
、submit
属性的Promise对象dialog
。dialog.submit()
关闭弹窗并触发Promise.resolve()dialog.cancel()
关闭弹窗并触发Promise.reject()dialog.visible
弹窗显隐的状态的属性,它的改变应仅通过cancel/submit触发
整套代码写下来,没有太多规则,就连title这种东西,我也是希望交给开发者自己维护。如果你觉得你们项目中就是还有一些重复的代码,可以二次封装,但是对于通用命令式弹窗来说,上面的方法就足够了,剩下的灵活性请交给h函数或者jsx。
总结
通过这样的设计,实现了两个层面的解耦
-
调用方法与弹窗UI的解耦。可以任意替换弹窗UI组件,且可以使用它提供的所有能力(属性、事件、插槽等)
-
弹窗组件与调用页面的解耦。不用再通过defineExpose来暴露组件方法,调用页面只需要关心调用弹窗的【Promise状态/有无返回值】来进行下一步操作
不管再复杂的应用场景,基于这套方法都能轻松实现命令式的弹窗调用了。
我一直喜欢站在使用者的角度去思考组件封装如何能够更简单、更灵活,所以欢迎各位掘友提出建议,一起讨论,一起成长~