提交 f4621cc6 编写于 作者: V vben

perf: perf loading logic

上级 0c2e72d2
......@@ -11,6 +11,10 @@
- i18n 支持 vscode `i18n-ally`插件
- 新增多级路由缓存示例
### ⚡ Performance Improvements
- 页面切换 loading 逻辑修改。对于已经加载过的页面不管有没有关闭,再次打开不会在显示 loading(已经打开过的页面在此打开速度比较快),刷新后恢复。
### 🎫 Chores
- 首屏 loading 修改
......
......@@ -76,7 +76,7 @@
position: absolute;
top: 0;
left: 0;
z-index: 1;
z-index: 300;
}
}
</style>
......@@ -7,8 +7,6 @@ import { appStore } from '/@/store/modules/app';
export function useMultipleTabSetting() {
const getMultipleTabSetting = computed(() => appStore.getProjectConfig.multiTabsSetting);
const getMax = computed(() => unref(getMultipleTabSetting).max);
const getShowMultipleTab = computed(() => unref(getMultipleTabSetting).show);
const getShowQuick = computed(() => unref(getMultipleTabSetting).showQuick);
......@@ -21,7 +19,6 @@ export function useMultipleTabSetting() {
setMultipleTabSetting,
getMultipleTabSetting,
getMax,
getShowMultipleTab,
getShowQuick,
};
......
......@@ -11,8 +11,8 @@ export function useTransitionSetting() {
const getOpenNProgress = computed(() => unref(getTransitionSetting)?.openNProgress);
const getOpenPageLoading = computed(() => {
return unref(getTransitionSetting)?.openPageLoading && unref(getEnableTransition);
const getOpenPageLoading = computed((): boolean => {
return !!unref(getTransitionSetting)?.openPageLoading;
});
const getBasicTransition = computed(() => unref(getTransitionSetting)?.basicTransition);
......
......@@ -11,10 +11,7 @@ export type RouteLocationRawEx = Omit<RouteLocationRaw, 'path'> & { path: PageEn
function handleError(e: Error) {
console.error(e);
// 101是为了 大于 打开时候设置的100延时防止闪动
setTimeout(() => {
appStore.commitPageLoadingState(false);
}, 101);
appStore.commitPageLoadingState(false);
}
// page switch
......
......@@ -12,10 +12,7 @@
&__loading {
position: absolute;
top: 200px;
z-index: @page-loading-z-index;
> .basic-loading {
margin-bottom: 15%;
}
}
}
......@@ -5,7 +5,7 @@ import { Loading } from '/@/components/Loading';
import { useRootSetting } from '/@/hooks/setting/useRootSetting';
import { useTransitionSetting } from '/@/hooks/setting/useTransitionSetting';
import PageLayout from '/@/layouts/page/index.vue';
import PageLayout from '/@/layouts/page/index';
export default defineComponent({
name: 'LayoutContent',
setup() {
......@@ -16,7 +16,12 @@ export default defineComponent({
return (
<div class={['layout-content', unref(getLayoutContentMode)]}>
{unref(getOpenPageLoading) && (
<Loading loading={unref(getPageLoading)} absolute class="layout-content__loading" />
<Loading
loading={unref(getPageLoading)}
background="rgba(240, 242, 245, 0.6)"
absolute
class="layout-content__loading"
/>
)}
<PageLayout />
</div>
......
......@@ -465,7 +465,6 @@ export default defineComponent({
baseHandler(HandlerEnum.OPEN_PAGE_LOADING, e);
},
def: unref(getOpenPageLoading),
disabled: !unref(getEnableTransition),
})}
{renderSwitchItem(t('layout.setting.switchAnimation'), {
......
......@@ -4,23 +4,18 @@
<template>
<div>
<router-view>
<template #default="{ Component, route }">
<transition v-bind="transitionEvent" :name="getName(route)" mode="out-in" appear>
<keep-alive v-if="openCache" :include="getCaches">
<component :max="getMax" :is="Component" :key="route.fullPath" />
</keep-alive>
<component v-else :max="getMax" :is="Component" :key="route.fullPath" />
</transition>
<template v-slot="{ Component, route }">
<keep-alive v-if="openCache" :include="getCaches">
<component :is="Component" :key="route.fullPath" />
</keep-alive>
<component v-else :is="Component" :key="route.fullPath" />
</template>
</router-view>
</div>
</template>
<script lang="ts">
import { computed, defineComponent, unref } from 'vue';
import { RouteLocationNormalized } from 'vue-router';
import { useTransition } from './useTransition';
import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
import { useRootSetting } from '/@/hooks/setting/useRootSetting';
import { useMultipleTabSetting } from '/@/hooks/setting/useMultipleTabSetting';
......@@ -28,43 +23,20 @@
import { useCache } from './useCache';
export default defineComponent({
props: {
isPage: {
type: Boolean,
},
},
setup(props) {
const { getCaches } = useCache(props.isPage);
setup() {
const { getCaches } = useCache(false);
const { getShowMenu } = useMenuSetting();
const { getShowMultipleTab } = useMultipleTabSetting();
const { getOpenKeepAlive } = useRootSetting();
const { getBasicTransition, getEnableTransition } = useTransitionSetting();
const { getMax } = useMultipleTabSetting();
const transitionEvent = useTransition();
const openCache = computed(() => unref(getOpenKeepAlive) && unref(getShowMenu));
function getName(route: RouteLocationNormalized) {
if (!unref(getEnableTransition)) {
return null;
}
const cacheTabs = unref(getCaches);
const isInCache = cacheTabs.includes(route.name as string);
const name = isInCache && route.meta.inTab ? 'fade-slide' : null;
return name || route.meta.transitionName || unref(getBasicTransition);
}
const openCache = computed(() => unref(getOpenKeepAlive) && unref(getShowMultipleTab));
return {
getCaches,
getMax,
transitionEvent,
getBasicTransition,
getName,
openCache,
getEnableTransition,
};
......
import type { FunctionalComponent } from 'vue';
import { computed, defineComponent, unref, Transition, KeepAlive } from 'vue';
import { RouterView, RouteLocation } from 'vue-router';
import FrameLayout from '/@/layouts/iframe/index.vue';
import { useRootSetting } from '/@/hooks/setting/useRootSetting';
import { useTransitionSetting } from '/@/hooks/setting/useTransitionSetting';
import { useCache } from './useCache';
import { useMultipleTabSetting } from '/@/hooks/setting/useMultipleTabSetting';
interface DefaultContext {
Component: FunctionalComponent;
route: RouteLocation;
}
export default defineComponent({
name: 'PageLayout',
setup() {
const { getCaches } = useCache(true);
const { getShowMultipleTab } = useMultipleTabSetting();
const { getOpenKeepAlive, getCanEmbedIFramePage } = useRootSetting();
const { getBasicTransition, getEnableTransition } = useTransitionSetting();
const openCache = computed(() => unref(getOpenKeepAlive) && unref(getShowMultipleTab));
return () => {
return (
<>
<RouterView>
{{
default: ({ Component, route }: DefaultContext) => {
// No longer show animations that are already in the tab
const cacheTabs = unref(getCaches);
const isInCache = cacheTabs.includes(route.name as string);
const name =
isInCache && route.meta.loaded && unref(getEnableTransition)
? 'fade-slide'
: null;
const renderComp = () => <Component key={route.fullPath} />;
const PageContent = unref(openCache) ? (
<KeepAlive>{renderComp()}</KeepAlive>
) : (
renderComp()
);
return unref(getEnableTransition) ? (
<Transition
name={name || route.meta.transitionName || unref(getBasicTransition)}
mode="out-in"
appear={true}
>
{() => PageContent}
</Transition>
) : (
PageContent
);
},
}}
</RouterView>
{unref(getCanEmbedIFramePage) && <FrameLayout />}
</>
);
};
},
});
<template>
<ParentLayout :isPage="true" />
<FrameLayout v-if="getCanEmbedIFramePage" />
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import FrameLayout from '/@/layouts/iframe/index.vue';
import { useRootSetting } from '/@/hooks/setting/useRootSetting';
import ParentLayout from '/@/layouts/parent/index.vue';
export default defineComponent({
components: { ParentLayout, FrameLayout },
setup() {
const { getCanEmbedIFramePage } = useRootSetting();
return { getCanEmbedIFramePage };
},
});
</script>
......@@ -10,9 +10,8 @@ export function useCache(isPage: boolean) {
const name = ref('');
const { currentRoute } = useRouter();
tryTsxEmit((instance: any) => {
const routeName = instance.ctx.$options.name;
tryTsxEmit((instance) => {
const routeName = instance.type.name;
if (routeName && ![ParentLayoutName].includes(routeName)) {
name.value = routeName;
} else {
......@@ -22,6 +21,7 @@ export function useCache(isPage: boolean) {
name.value = matched[len - 2].name as string;
}
});
const { getOpenKeepAlive } = useRootSetting();
const getCaches = computed((): string[] => {
......
import { useTransitionSetting } from '/@/hooks/setting/useTransitionSetting';
import { appStore } from '/@/store/modules/app';
import { tryOnUnmounted } from '/@/utils/helper/vueHelper';
export function useTransition() {
function handleAfterEnter() {
const { getOpenPageLoading, getEnableTransition } = useTransitionSetting();
if (!getOpenPageLoading.value || !getEnableTransition.value) return;
// Close loading after the route switching animation ends
appStore.setPageLoadingAction(false);
}
tryOnUnmounted(() => {
handleAfterEnter();
stop();
});
return {
onAfterEnter: handleAfterEnter,
};
}
import type { AppRouteRecordRaw } from '/@/router/types';
import ParentLayout from '/@/layouts/parent/index.vue';
import ParentLayout from '/@/layouts/page/ParentView.vue';
const EXCEPTION_COMPONENT = () => import('../views/sys/exception/Exception');
......@@ -8,11 +8,6 @@ const EXCEPTION_COMPONENT = () => import('../views/sys/exception/Exception');
*/
export const LAYOUT = () => import('/@/layouts/default/index');
/**
* @description: page-layout
*/
export const PAGE_LAYOUT_COMPONENT = () => import('/@/layouts/page/index.vue');
/**
* @description: page-layout
*/
......
import type { Router } from 'vue-router';
import { isNavigationFailure, Router } from 'vue-router';
import { Modal, notification } from 'ant-design-vue';
......@@ -8,7 +8,7 @@ import { createPageLoadingGuard } from './pageLoadingGuard';
import { useGlobSetting, useProjectSetting } from '/@/hooks/setting';
import { getIsOpenTab, getRoute } from '/@/router/helper/routeHelper';
import { getRoute } from '/@/router/helper/routeHelper';
import { setTitle } from '/@/utils/browser';
import { AxiosCanceler } from '/@/utils/http/axios/axiosCancel';
......@@ -24,13 +24,10 @@ export function createGuard(router: Router) {
if (removeAllHttpPending) {
axiosCanceler = new AxiosCanceler();
}
const loadedPageMap = new Map<string, boolean>();
createPageLoadingGuard(router);
router.beforeEach(async (to) => {
// Determine whether the tab has been opened
const isOpen = getIsOpenTab(to.fullPath);
to.meta.inTab = isOpen;
to.meta.loaded = !!loadedPageMap.get(to.path);
// Notify routing changes
tabStore.commitLastChangeRouteState(getRoute(to));
......@@ -47,11 +44,17 @@ export function createGuard(router: Router) {
return true;
});
router.afterEach((to) => {
router.afterEach((to, from, failure) => {
loadedPageMap.set(to.path, true);
const { t } = useI18n();
// change html title
to.name !== REDIRECT_NAME && setTitle(t(to.meta.title), globSetting.title);
if (isNavigationFailure(failure)) {
console.error('router navigation failed:', failure);
}
});
createPageLoadingGuard(router);
createProgressGuard(router);
createPermissionGuard(router);
}
import type { Router } from 'vue-router';
import { tabStore } from '/@/store/modules/tab';
import { appStore } from '/@/store/modules/app';
import { userStore } from '/@/store/modules/user';
import { getParams } from '/@/router/helper/routeHelper';
import { useTransitionSetting } from '/@/hooks/setting/useTransitionSetting';
import { unref } from 'vue';
const { getOpenPageLoading, getEnableTransition } = useTransitionSetting();
const { getOpenPageLoading } = useTransitionSetting();
export function createPageLoadingGuard(router: Router) {
let isFirstLoad = true;
router.beforeEach(async (to) => {
const { openKeepAlive, multiTabsSetting: { show } = {} } = appStore.getProjectConfig;
if (!userStore.getTokenState) {
return true;
}
if (!unref(getEnableTransition) && unref(getOpenPageLoading)) {
appStore.commitPageLoadingState(true);
if (to.meta.loaded) {
return true;
}
if (show && openKeepAlive && !isFirstLoad) {
const tabList = tabStore.getTabsState;
const isOpen = tabList.some((tab) => tab.path === to.path);
appStore.setPageLoadingAction(!isOpen);
} else {
if (unref(getOpenPageLoading)) {
appStore.setPageLoadingAction(true);
return true;
}
return true;
});
router.afterEach(async (to, from) => {
const realToPath = to.path.replace(getParams(to), '');
const realFormPath = from.path.replace(getParams(from), '');
if (
(!unref(getEnableTransition) && unref(getOpenPageLoading)) ||
isFirstLoad ||
to.meta.afterCloseLoading ||
realToPath === realFormPath
) {
router.afterEach(async () => {
if (unref(getOpenPageLoading)) {
setTimeout(() => {
appStore.commitPageLoadingState(false);
}, 110);
isFirstLoad = false;
}, 300);
}
return true;
});
}
......@@ -10,12 +10,14 @@ const { getOpenNProgress } = useTransitionSetting();
export function createProgressGuard(router: Router) {
router.beforeEach(async (to) => {
!to.meta.inTab && unref(getOpenNProgress) && NProgress.start();
if (to.meta.loaded) return true;
unref(getOpenNProgress) && NProgress.start();
return true;
});
router.afterEach(async (to) => {
!to.meta.inTab && unref(getOpenNProgress) && NProgress.done();
if (to.meta.loaded) return true;
unref(getOpenNProgress) && NProgress.done();
return true;
});
}
import type { AppRouteModule, AppRouteRecordRaw } from '/@/router/types';
import type { RouteLocationNormalized, RouteRecordNormalized } from 'vue-router';
import { appStore } from '/@/store/modules/app';
import { tabStore } from '/@/store/modules/tab';
import { getParentLayout, LAYOUT } from '/@/router/constant';
import dynamicImport from './dynamicImport';
import { cloneDeep } from 'lodash-es';
......@@ -48,20 +46,6 @@ export function transformObjToRoute<T = AppRouteModule>(routeList: AppRouteModul
return (routeList as unknown) as T[];
}
/**
* Determine whether the tab has been opened
* @param toPath
*/
export function getIsOpenTab(toPath: string) {
const { openKeepAlive, multiTabsSetting: { show } = {} } = appStore.getProjectConfig;
if (show && openKeepAlive) {
const tabList = tabStore.getTabsState;
return tabList.some((tab) => tab.path === toPath);
}
return false;
}
export function getParams(data: any = {}) {
const { params = {} } = data;
let ret = '';
......
......@@ -33,4 +33,8 @@ export function setupRouter(app: App<Element>) {
createGuard(router);
}
router.onError((error) => {
console.error(error);
});
export default router;
......@@ -27,8 +27,6 @@ export interface RouteMeta {
// close loading
afterCloseLoading?: boolean;
// Is it in the tab
inTab?: boolean;
// Carrying parameters
carryParam?: boolean;
......
......@@ -119,8 +119,6 @@ const setting: ProjectConfig = {
canDrag: true,
// Turn on quick actions
showQuick: true,
// Maximum number of tab cache
max: 12,
},
// Transition Setting
......
......@@ -105,7 +105,7 @@ class App extends VuexModule {
// Prevent flicker
timeId = setTimeout(() => {
this.commitPageLoadingState(loading);
}, 100);
}, 50);
} else {
this.commitPageLoadingState(loading);
clearTimeout(timeId);
......
import { appStore } from './app';
import type {
LoginParams,
GetUserInfoByUserIdModel,
......@@ -107,12 +106,7 @@ class User extends VuexModule {
// const name = FULL_PAGE_NOT_FOUND_ROUTE.name;
// name && router.removeRoute(name);
goHome &&
(await router.push(PageEnum.BASE_HOME).then(() => {
setTimeout(() => {
appStore.commitPageLoadingState(false);
}, 30);
}));
goHome && router.push(PageEnum.BASE_HOME);
return userInfo;
} catch (error) {
return null;
......
......@@ -29,8 +29,6 @@ export interface MultiTabsSetting {
// 开启快速操作
showQuick: boolean;
canDrag: boolean;
// 缓存最大数量
max: number;
}
export interface HeaderSetting {
......
......@@ -4,15 +4,11 @@
<script lang="ts">
import { defineComponent, unref } from 'vue';
import { appStore } from '/@/store/modules/app';
import { useRouter } from 'vue-router';
import { useTransitionSetting } from '/@/hooks/setting/useTransitionSetting';
export default defineComponent({
name: 'Redirect',
setup() {
const { currentRoute, replace } = useRouter();
const { getOpenPageLoading, getEnableTransition } = useTransitionSetting();
const { params, query } = unref(currentRoute);
const { path } = params;
......@@ -21,12 +17,7 @@
path: '/' + _path,
query,
});
// close loading
if (unref(getEnableTransition) && unref(getOpenPageLoading)) {
setTimeout(() => {
appStore.setPageLoadingAction(false);
}, 0);
}
return {};
},
});
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册