通用命令式弹窗的最佳实践

1,527 阅读2分钟

前言

这是一个基于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对象
  • 将弹窗控制抽象为一个拥有visiblecancelsubmit属性的Promise对象dialog
    • dialog.submit() 关闭弹窗并触发Promise.resolve()
    • dialog.cancel() 关闭弹窗并触发Promise.reject()
    • dialog.visible 弹窗显隐的状态的属性,它的改变应仅通过cancel/submit触发

整套代码写下来,没有太多规则,就连title这种东西,我也是希望交给开发者自己维护。如果你觉得你们项目中就是还有一些重复的代码,可以二次封装,但是对于通用命令式弹窗来说,上面的方法就足够了,剩下的灵活性请交给h函数或者jsx。

总结

通过这样的设计,实现了两个层面的解耦

  1. 调用方法与弹窗UI的解耦。可以任意替换弹窗UI组件,且可以使用它提供的所有能力(属性、事件、插槽等)

  2. 弹窗组件与调用页面的解耦。不用再通过defineExpose来暴露组件方法,调用页面只需要关心调用弹窗的【Promise状态/有无返回值】来进行下一步操作

不管再复杂的应用场景,基于这套方法都能轻松实现命令式的弹窗调用了。

我一直喜欢站在使用者的角度去思考组件封装如何能够更简单、更灵活,所以欢迎各位掘友提出建议,一起讨论,一起成长~