mirror of
https://github.com/vbenjs/vue-vben-admin.git
synced 2025-08-26 16:46:19 +08:00
perf: improve destroyOnClose
for VbenModal
(#5935)
* perf: 优化Vben Modal destroyOnClose,解决destroyOnClose=false,Modal依旧会被销毁的问题 影响范围(重要):destroyOnClose默认为true,这会导致所有的modal都会默认渲染到body radix-vue Dialog组件默认会销毁挂载的组件,所以即使destroyOnClose=false,Modal依旧会被销毁的问题 对于一些大表单重复渲染导致卡顿,ApiComponent也会频繁的加载数据 * fix: modal closing animation --------- Co-authored-by: Netfan <netfan@foxmail.com>
This commit is contained in:
@@ -44,6 +44,7 @@ export class ModalApi {
|
||||
confirmDisabled: false,
|
||||
confirmLoading: false,
|
||||
contentClass: '',
|
||||
destroyOnClose: true,
|
||||
draggable: false,
|
||||
footer: true,
|
||||
footerClass: '',
|
||||
|
@@ -60,6 +60,10 @@ export interface ModalProps {
|
||||
* 弹窗描述
|
||||
*/
|
||||
description?: string;
|
||||
/**
|
||||
* 在关闭时销毁弹窗
|
||||
*/
|
||||
destroyOnClose?: boolean;
|
||||
/**
|
||||
* 是否可拖拽
|
||||
* @default false
|
||||
@@ -153,10 +157,6 @@ export interface ModalApiOptions extends ModalState {
|
||||
* 独立的弹窗组件
|
||||
*/
|
||||
connectedComponent?: Component;
|
||||
/**
|
||||
* 在关闭时销毁弹窗。仅在使用 connectedComponent 时有效
|
||||
*/
|
||||
destroyOnClose?: boolean;
|
||||
/**
|
||||
* 关闭前的回调,返回 false 可以阻止关闭
|
||||
* @returns
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<script lang="ts" setup>
|
||||
import type { ExtendedModalApi, ModalProps } from './modal';
|
||||
|
||||
import { computed, nextTick, provide, ref, useId, watch } from 'vue';
|
||||
import { computed, nextTick, provide, ref, unref, useId, watch } from 'vue';
|
||||
|
||||
import {
|
||||
useIsMobile,
|
||||
@@ -34,6 +34,7 @@ interface Props extends ModalProps {
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
appendToMain: false,
|
||||
destroyOnClose: true,
|
||||
modalApi: undefined,
|
||||
});
|
||||
|
||||
@@ -67,6 +68,7 @@ const {
|
||||
confirmText,
|
||||
contentClass,
|
||||
description,
|
||||
destroyOnClose,
|
||||
draggable,
|
||||
footer: showFooter,
|
||||
footerClass,
|
||||
@@ -100,10 +102,15 @@ const { dragging, transform } = useModalDraggable(
|
||||
shouldDraggable,
|
||||
);
|
||||
|
||||
const firstOpened = ref(false);
|
||||
const isClosed = ref(false);
|
||||
|
||||
watch(
|
||||
() => state?.value?.isOpen,
|
||||
async (v) => {
|
||||
if (v) {
|
||||
isClosed.value = false;
|
||||
if (!firstOpened.value) firstOpened.value = true;
|
||||
await nextTick();
|
||||
if (!contentRef.value) return;
|
||||
const innerContentRef = contentRef.value.getContentRef();
|
||||
@@ -113,6 +120,7 @@ watch(
|
||||
dialogRef.value.style.transform = `translate(${offsetX}px, ${offsetY}px)`;
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
watch(
|
||||
@@ -176,6 +184,15 @@ const getAppendTo = computed(() => {
|
||||
? `#${ELEMENT_ID_MAIN_CONTENT}>div:not(.absolute)>div`
|
||||
: undefined;
|
||||
});
|
||||
|
||||
const getForceMount = computed(() => {
|
||||
return !unref(destroyOnClose);
|
||||
});
|
||||
|
||||
function handleClosed() {
|
||||
isClosed.value = true;
|
||||
props.modalApi?.onClosed();
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<Dialog
|
||||
@@ -197,9 +214,11 @@ const getAppendTo = computed(() => {
|
||||
shouldFullscreen,
|
||||
'top-1/2 !-translate-y-1/2': centered && !shouldFullscreen,
|
||||
'duration-300': !dragging,
|
||||
hidden: isClosed,
|
||||
},
|
||||
)
|
||||
"
|
||||
:force-mount="getForceMount"
|
||||
:modal="modal"
|
||||
:open="state?.isOpen"
|
||||
:show-close="closable"
|
||||
@@ -207,7 +226,7 @@ const getAppendTo = computed(() => {
|
||||
:overlay-blur="overlayBlur"
|
||||
close-class="top-3"
|
||||
@close-auto-focus="handleFocusOutside"
|
||||
@closed="() => modalApi?.onClosed()"
|
||||
@closed="handleClosed"
|
||||
:close-disabled="submitting"
|
||||
@escape-key-down="escapeKeyDown"
|
||||
@focus-outside="handleFocusOutside"
|
||||
|
@@ -1,14 +1,6 @@
|
||||
import type { ExtendedModalApi, ModalApiOptions, ModalProps } from './modal';
|
||||
|
||||
import {
|
||||
defineComponent,
|
||||
h,
|
||||
inject,
|
||||
nextTick,
|
||||
provide,
|
||||
reactive,
|
||||
ref,
|
||||
} from 'vue';
|
||||
import { defineComponent, h, inject, nextTick, provide, reactive } from 'vue';
|
||||
|
||||
import { useStore } from '@vben-core/shared/store';
|
||||
|
||||
@@ -32,7 +24,6 @@ export function useVbenModal<TParentModalProps extends ModalProps = ModalProps>(
|
||||
const { connectedComponent } = options;
|
||||
if (connectedComponent) {
|
||||
const extendedApi = reactive({});
|
||||
const isModalReady = ref(true);
|
||||
const Modal = defineComponent(
|
||||
(props: TParentModalProps, { attrs, slots }) => {
|
||||
provide(USER_MODAL_INJECT_KEY, {
|
||||
@@ -42,11 +33,6 @@ export function useVbenModal<TParentModalProps extends ModalProps = ModalProps>(
|
||||
Object.setPrototypeOf(extendedApi, api);
|
||||
},
|
||||
options,
|
||||
async reCreateModal() {
|
||||
isModalReady.value = false;
|
||||
await nextTick();
|
||||
isModalReady.value = true;
|
||||
},
|
||||
});
|
||||
checkProps(extendedApi as ExtendedModalApi, {
|
||||
...props,
|
||||
@@ -55,7 +41,7 @@ export function useVbenModal<TParentModalProps extends ModalProps = ModalProps>(
|
||||
});
|
||||
return () =>
|
||||
h(
|
||||
isModalReady.value ? connectedComponent : 'div',
|
||||
connectedComponent,
|
||||
{
|
||||
...props,
|
||||
...attrs,
|
||||
@@ -84,14 +70,6 @@ export function useVbenModal<TParentModalProps extends ModalProps = ModalProps>(
|
||||
injectData.options?.onOpenChange?.(isOpen);
|
||||
};
|
||||
|
||||
const onClosed = mergedOptions.onClosed;
|
||||
|
||||
mergedOptions.onClosed = () => {
|
||||
onClosed?.();
|
||||
if (mergedOptions.destroyOnClose) {
|
||||
injectData.reCreateModal?.();
|
||||
}
|
||||
};
|
||||
const api = new ModalApi(mergedOptions);
|
||||
|
||||
const extendedApi: ExtendedModalApi = api as never;
|
||||
|
Reference in New Issue
Block a user