Commit f4621cc66411d8ff4ca852b548a79cd3da9be1ce

Authored by vben
1 parent 0c2e72d2

perf: perf loading logic

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