vue3优雅封装弹窗

7,428 阅读2分钟

弹窗是前端中一个很常见的业务功能,通常弹窗分两个页面现实,这两个页面是父子关系。

实现需要的vue-composition-api:inject, provide, ref, watchEffect

这个hooks一共只有两个方法:控制打开弹窗(openModal)、控制关闭弹窗(closeModal)

openModal

/**
 * 控制打开弹窗
 * @param modelName 相对应的弹窗组件名(这里是自己定义的一个接口,一是为了编码代码时候友好提示,十是为了防止代码出错)
 * @returns openAction 打开弹窗方法
 */
export function openModal(modelName: keyof ModalName) {
    const name = ref<boolean>(false)
    provide(modelName, name);
    /**
     * 打开弹窗
     * @param value 弹窗名
     */
    function openAction() {
        name.value = true;
    }
    return { openAction }
}
/**
 * 控制关闭弹窗
 * @param modelName 相对应的弹窗组件名
 * @returns 
 */
export function closeModal(modelName: keyof ModalName) {
    // 控制弹窗 flag
    const visible = ref<boolean>(false);
    const show = inject(modelName, ref<boolean>(false))
    watchEffect(() => {
        visible.value = show.value
    })
    /**
     * 取消弹窗
     */
    function cancel() {
        visible.value = false;
        show.value = false
    }
    /**
     * 带有回调函数关闭弹窗
     * @param callback 
     */
    function handleOk(callback: Function) {
        cancel();
        callback && callback()
    }
    return { visible, cancel, handleOk }
}

ModalName 是我们定义好的接口名,这样做一是了防止出错,二是了有友好提示

在父组件里引入openModal,并在setup里调用openModal方法,传入参数就是我们定义好的,弹窗组件名。

在子组件里引入closeModal,并在setup里调用closeModall方法,传入参数就是我们定义好的,弹窗组件名。

这个时候在父组件里provide弹窗名,在子组件inject弹窗名,这样父子组件就建立了一个通信渠道

当父组件需要弹窗的时候,调用openAction方法,这个时候就会更改provide的value为true(默认是false),子组 件里show(接受inject的值)的value就会变化,而我们通过watchEffect追踪其依赖show的依赖,并更改控制弹窗 的flag—visible,这样就达到了控制打开弹窗。

关闭弹窗很简单,设置控制弹窗 的flag——visible为false,并把show.value重置为默认值false

个人实现示例github.com/Buzhifanji/…

父组件

<template>
  <div class="home">
    <div @click="openNotice">消息-弹窗</div>
    <Notice />
  </div>
</template>
<script lang="ts">
import { defineComponent } from "vue";
import { openModal } from "@/setup/controlModal/index"; // 控制弹窗逻辑
import Notice from "./notice.vue";

export default defineComponent({
  name: "home",
  components: {
    Notice,
  },
  setup() {
      // 打开弹窗
    const { openAction } = openModal("notice");
    return { openAction };
  }
});
</script>

子组件

<template>
  <div class="modal">
    <a-modal class="notice"
             v-model:visible="visible"
             @cancel="cancel"
             width="890px"
             title="弹窗"
             :footer="null"
             @ok="handleOk">
      我是一个消息弹窗
    </a-modal>
  </div>
</template>

<script lang="ts">
import { defineComponent } from "vue";
import { closeModal } from "../setup/controlModal/index";

export default defineComponent({
  name: "modal",
  components: {},
  setup() {
      // visible 是否弹窗flag
      // cancel 关闭弹窗
      // 带有回调函数关闭弹窗(预留需要关闭弹窗进行其他操作)
    const { visible, cancel, handleOk } = closeModal("notice");
    return { visible, cancel };
  }
});
</script>

父组件调用openModal,子组件调用closeModal,封装后代码简洁,优雅