提交 327d71b8 编写于 作者: V vben

perf(router): reduce the number of guard files

上级 941ad597
...@@ -129,6 +129,7 @@ ...@@ -129,6 +129,7 @@
"qrcode", "qrcode",
"sider", "sider",
"pinia", "pinia",
"sider" "sider",
"nprogress"
] ]
} }
...@@ -10,7 +10,6 @@ ...@@ -10,7 +10,6 @@
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import { ConfigProvider } from 'ant-design-vue'; import { ConfigProvider } from 'ant-design-vue';
import { AppProvider } from '/@/components/Application'; import { AppProvider } from '/@/components/Application';
import { useTitle } from '/@/hooks/web/useTitle'; import { useTitle } from '/@/hooks/web/useTitle';
import { useLocale } from '/@/locales/useLocale'; import { useLocale } from '/@/locales/useLocale';
......
...@@ -137,7 +137,7 @@ ...@@ -137,7 +137,7 @@
watch( watch(
() => props.visible, () => props.visible,
(newVal, oldVal) => { (newVal, oldVal) => {
if (newVal != oldVal) visibleRef.value = newVal; if (newVal !== oldVal) visibleRef.value = newVal;
}, },
{ deep: true } { deep: true }
); );
......
...@@ -19,25 +19,18 @@ ...@@ -19,25 +19,18 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import type { MenuState } from './types'; import type { MenuState } from './types';
import { computed, defineComponent, unref, reactive, watch, toRefs, ref } from 'vue'; import { computed, defineComponent, unref, reactive, watch, toRefs, ref } from 'vue';
import { Menu } from 'ant-design-vue'; import { Menu } from 'ant-design-vue';
import BasicSubMenuItem from './components/BasicSubMenuItem.vue'; import BasicSubMenuItem from './components/BasicSubMenuItem.vue';
import { MenuModeEnum, MenuTypeEnum } from '/@/enums/menuEnum'; import { MenuModeEnum, MenuTypeEnum } from '/@/enums/menuEnum';
import { useOpenKeys } from './useOpenKeys'; import { useOpenKeys } from './useOpenKeys';
import { RouteLocationNormalizedLoaded, useRouter } from 'vue-router'; import { RouteLocationNormalizedLoaded, useRouter } from 'vue-router';
import { isFunction } from '/@/utils/is'; import { isFunction } from '/@/utils/is';
import { basicProps } from './props'; import { basicProps } from './props';
import { useMenuSetting } from '/@/hooks/setting/useMenuSetting'; import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
import { REDIRECT_NAME } from '/@/router/constant'; import { REDIRECT_NAME } from '/@/router/constant';
import { useDesign } from '/@/hooks/web/useDesign'; import { useDesign } from '/@/hooks/web/useDesign';
import { getCurrentParentPath } from '/@/router/menus'; import { getCurrentParentPath } from '/@/router/menus';
import { listenerRouteChange } from '/@/logics/mitt/routeChange'; import { listenerRouteChange } from '/@/logics/mitt/routeChange';
import { getAllParentPath } from '/@/router/helper/menuHelper'; import { getAllParentPath } from '/@/router/helper/menuHelper';
......
...@@ -5,6 +5,7 @@ import { ...@@ -5,6 +5,7 @@ import {
// Need // Need
Button as AntButton, Button as AntButton,
Input, Input,
Layout,
} from 'ant-design-vue'; } from 'ant-design-vue';
const compList = [AntButton.Group]; const compList = [AntButton.Group];
...@@ -14,5 +15,5 @@ export function registerGlobComp(app: App) { ...@@ -14,5 +15,5 @@ export function registerGlobComp(app: App) {
app.component(comp.name || comp.displayName, comp); app.component(comp.name || comp.displayName, comp);
}); });
app.use(Input).use(Button); app.use(Input).use(Button).use(Layout);
} }
import type { Menu } from '/@/router/types'; import type { Menu } from '/@/router/types';
import type { Ref } from 'vue'; import type { Ref } from 'vue';
import { watch, unref, ref, computed } from 'vue'; import { watch, unref, ref, computed } from 'vue';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import { MenuSplitTyeEnum } from '/@/enums/menuEnum'; import { MenuSplitTyeEnum } from '/@/enums/menuEnum';
import { useThrottleFn } from '@vueuse/core'; import { useThrottleFn } from '@vueuse/core';
import { useMenuSetting } from '/@/hooks/setting/useMenuSetting'; import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
import { getChildrenMenus, getCurrentParentPath, getMenus, getShallowMenus } from '/@/router/menus'; import { getChildrenMenus, getCurrentParentPath, getMenus, getShallowMenus } from '/@/router/menus';
import { usePermissionStore } from '/@/store/modules/permission'; import { usePermissionStore } from '/@/store/modules/permission';
import { useAppInject } from '/@/hooks/web/useAppInject'; import { useAppInject } from '/@/hooks/web/useAppInject';
......
...@@ -81,24 +81,19 @@ ...@@ -81,24 +81,19 @@
import type { Menu } from '/@/router/types'; import type { Menu } from '/@/router/types';
import type { CSSProperties } from 'vue'; import type { CSSProperties } from 'vue';
import type { RouteLocationNormalized } from 'vue-router'; import type { RouteLocationNormalized } from 'vue-router';
import { defineComponent, onMounted, ref, computed, unref } from 'vue'; import { defineComponent, onMounted, ref, computed, unref } from 'vue';
import { ScrollContainer } from '/@/components/Container'; import { ScrollContainer } from '/@/components/Container';
import { SimpleMenuTag } from '/@/components/SimpleMenu'; import { SimpleMenuTag } from '/@/components/SimpleMenu';
import { Icon } from '/@/components/Icon'; import { Icon } from '/@/components/Icon';
import { AppLogo } from '/@/components/Application'; import { AppLogo } from '/@/components/Application';
import Trigger from '../trigger/HeaderTrigger.vue'; import Trigger from '../trigger/HeaderTrigger.vue';
import { useMenuSetting } from '/@/hooks/setting/useMenuSetting'; import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
import { useDragLine } from './useLayoutSider'; import { useDragLine } from './useLayoutSider';
import { useGlobSetting } from '/@/hooks/setting'; import { useGlobSetting } from '/@/hooks/setting';
import { useDesign } from '/@/hooks/web/useDesign'; import { useDesign } from '/@/hooks/web/useDesign';
import { useI18n } from '/@/hooks/web/useI18n'; import { useI18n } from '/@/hooks/web/useI18n';
import { useGo } from '/@/hooks/web/usePage'; import { useGo } from '/@/hooks/web/usePage';
import { SIDE_BAR_SHOW_TIT_MINI_WIDTH, SIDE_BAR_MINI_WIDTH } from '/@/enums/appEnum'; import { SIDE_BAR_SHOW_TIT_MINI_WIDTH, SIDE_BAR_MINI_WIDTH } from '/@/enums/appEnum';
import clickOutside from '/@/directives/clickOutside'; import clickOutside from '/@/directives/clickOutside';
import { getShallowMenus, getChildrenMenus, getCurrentParentPath } from '/@/router/menus'; import { getShallowMenus, getChildrenMenus, getCurrentParentPath } from '/@/router/menus';
import { listenerRouteChange } from '/@/logics/mitt/routeChange'; import { listenerRouteChange } from '/@/logics/mitt/routeChange';
......
import '/@/design/index.less'; import '/@/design/index.less';
import '/@/design/tailwind.css'; import '/@/design/tailwind.css';
// Register icon sprite // Register icon sprite
import 'virtual:svg-icons-register'; import 'virtual:svg-icons-register';
import App from './App.vue'; import App from './App.vue';
import { createApp } from 'vue'; import { createApp } from 'vue';
import { initAppConfigStore } from '/@/logics/initAppConfig'; import { initAppConfigStore } from '/@/logics/initAppConfig';
...@@ -42,7 +40,7 @@ async function bootstrap() { ...@@ -42,7 +40,7 @@ async function bootstrap() {
setupRouter(app); setupRouter(app);
// router-guard // router-guard
setupRouterGuard(); setupRouterGuard(router);
// Register global directive // Register global directive
setupGlobDirectives(app); setupGlobDirectives(app);
......
import type { Router } from 'vue-router';
import { AxiosCanceler } from '/@/utils/http/axios/axiosCancel';
import projectSetting from '/@/settings/projectSetting';
/**
* The interface used to close the current page to complete the request when the route is switched
* @param router
*/
export function createHttpGuard(router: Router) {
const { removeAllHttpPending } = projectSetting;
let axiosCanceler: Nullable<AxiosCanceler>;
if (removeAllHttpPending) {
axiosCanceler = new AxiosCanceler();
}
router.beforeEach(async () => {
// Switching the route will delete the previous request
axiosCanceler?.removeAllPending();
return true;
});
}
import { router } from '/@/router'; import type { Router, RouteLocationNormalized } from 'vue-router';
import { useAppStoreWidthOut } from '/@/store/modules/app';
import { createProgressGuard } from './progressGuard'; import { useUserStoreWidthOut } from '/@/store/modules/user';
import { useTransitionSetting } from '/@/hooks/setting/useTransitionSetting';
import { AxiosCanceler } from '/@/utils/http/axios/axiosCancel';
import { Modal, notification } from 'ant-design-vue';
import { warn } from '/@/utils/log';
import { unref } from 'vue';
import { setRouteChange } from '/@/logics/mitt/routeChange';
import { createPermissionGuard } from './permissionGuard'; import { createPermissionGuard } from './permissionGuard';
import { createPageLoadingGuard } from './pageLoadingGuard';
import { createMessageGuard } from './messageGuard';
import { createScrollGuard } from './scrollGuard';
import { createHttpGuard } from './httpGuard';
import { createPageGuard } from './pageGuard';
import { createStateGuard } from './stateGuard'; import { createStateGuard } from './stateGuard';
import nProgress from 'nprogress';
import projectSetting from '/@/settings/projectSetting';
export function setupRouterGuard() { // Don't change the order of creation
export function setupRouterGuard(router: Router) {
createPageGuard(router); createPageGuard(router);
createPageLoadingGuard(router); createPageLoadingGuard(router);
createHttpGuard(router); createHttpGuard(router);
...@@ -19,3 +23,123 @@ export function setupRouterGuard() { ...@@ -19,3 +23,123 @@ export function setupRouterGuard() {
createPermissionGuard(router); createPermissionGuard(router);
createStateGuard(router); createStateGuard(router);
} }
/**
* Hooks for handling page state
*/
function createPageGuard(router: Router) {
const loadedPageMap = new Map<string, boolean>();
router.beforeEach(async (to) => {
// The page has already been loaded, it will be faster to open it again, you don’t need to do loading and other processing
to.meta.loaded = !!loadedPageMap.get(to.path);
// Notify routing changes
setRouteChange(to);
return true;
});
router.afterEach((to) => {
loadedPageMap.set(to.path, true);
});
}
// Used to handle page loading status
function createPageLoadingGuard(router: Router) {
const userStore = useUserStoreWidthOut();
const appStore = useAppStoreWidthOut();
const { getOpenPageLoading } = useTransitionSetting();
router.beforeEach(async (to) => {
if (!userStore.getToken) {
return true;
}
if (to.meta.loaded) {
return true;
}
if (unref(getOpenPageLoading)) {
appStore.setPageLoadingAction(true);
return true;
}
return true;
});
router.afterEach(async () => {
if (unref(getOpenPageLoading)) {
// TODO Looking for a better way
// The timer simulates the loading time to prevent flashing too fast,
setTimeout(() => {
appStore.setPageLoading(false);
}, 220);
}
return true;
});
}
/**
* The interface used to close the current page to complete the request when the route is switched
* @param router
*/
function createHttpGuard(router: Router) {
const { removeAllHttpPending } = projectSetting;
let axiosCanceler: Nullable<AxiosCanceler>;
if (removeAllHttpPending) {
axiosCanceler = new AxiosCanceler();
}
router.beforeEach(async () => {
// Switching the route will delete the previous request
axiosCanceler?.removeAllPending();
return true;
});
}
// Routing switch back to the top
function createScrollGuard(router: Router) {
const isHash = (href: string) => {
return /^#/.test(href);
};
const body = document.body;
router.afterEach(async (to) => {
// scroll top
isHash((to as RouteLocationNormalized & { href: string })?.href) && body.scrollTo(0, 0);
return true;
});
}
/**
* Used to close the message instance when the route is switched
* @param router
*/
export function createMessageGuard(router: Router) {
const { closeMessageOnSwitch } = projectSetting;
router.beforeEach(async () => {
try {
if (closeMessageOnSwitch) {
Modal.destroyAll();
notification.destroy();
}
} catch (error) {
warn('message guard error:' + error);
}
return true;
});
}
export function createProgressGuard(router: Router) {
const { getOpenNProgress } = useTransitionSetting();
router.beforeEach(async (to) => {
if (to.meta.loaded) {
return true;
}
unref(getOpenNProgress) && nProgress.start();
return true;
});
router.afterEach(async () => {
unref(getOpenNProgress) && nProgress.done();
return true;
});
}
import type { Router } from 'vue-router';
import { Modal, notification } from 'ant-design-vue';
import projectSetting from '/@/settings/projectSetting';
import { warn } from '/@/utils/log';
/**
* Used to close the message instance when the route is switched
* @param router
*/
export function createMessageGuard(router: Router) {
const { closeMessageOnSwitch } = projectSetting;
router.beforeEach(async () => {
try {
if (closeMessageOnSwitch) {
Modal.destroyAll();
notification.destroy();
}
} catch (error) {
warn('message guard error:' + error);
}
return true;
});
}
import type { Router } from 'vue-router';
import { setRouteChange } from '/@/logics/mitt/routeChange';
export function createPageGuard(router: Router) {
const loadedPageMap = new Map<string, boolean>();
router.beforeEach(async (to) => {
to.meta.loaded = !!loadedPageMap.get(to.path);
// Notify routing changes
setRouteChange(to);
return true;
});
router.afterEach((to) => {
loadedPageMap.set(to.path, true);
});
}
import type { Router } from 'vue-router';
import { useAppStoreWidthOut } from '/@/store/modules/app';
import { useUserStoreWidthOut } from '/@/store/modules/user';
import { useTransitionSetting } from '/@/hooks/setting/useTransitionSetting';
import { unref } from 'vue';
export function createPageLoadingGuard(router: Router) {
const userStore = useUserStoreWidthOut();
const appStore = useAppStoreWidthOut();
const { getOpenPageLoading } = useTransitionSetting();
router.beforeEach(async (to) => {
if (!userStore.getToken) {
return true;
}
if (to.meta.loaded) {
return true;
}
if (unref(getOpenPageLoading)) {
appStore.setPageLoadingAction(true);
return true;
}
return true;
});
router.afterEach(async () => {
if (unref(getOpenPageLoading)) {
setTimeout(() => {
appStore.setPageLoading(false);
}, 220);
}
return true;
});
}
...@@ -32,13 +32,11 @@ export function createPermissionGuard(router: Router) { ...@@ -32,13 +32,11 @@ export function createPermissionGuard(router: Router) {
// token does not exist // token does not exist
if (!token) { if (!token) {
// You can access without permission. You need to set the routing meta.ignoreAuth to true // You can access without permission. You need to set the routing meta.ignoreAuth to true
if ( if (to.meta.ignoreAuth) {
to.meta.ignoreAuth
// || to.name === FULL_PAGE_NOT_FOUND_ROUTE.name
) {
next(); next();
return; return;
} }
// redirect login page // redirect login page
const redirectData: { path: string; replace: boolean; query?: Recordable<string> } = { const redirectData: { path: string; replace: boolean; query?: Recordable<string> } = {
path: LOGIN_PATH, path: LOGIN_PATH,
...@@ -53,10 +51,12 @@ export function createPermissionGuard(router: Router) { ...@@ -53,10 +51,12 @@ export function createPermissionGuard(router: Router) {
next(redirectData); next(redirectData);
return; return;
} }
if (permissionStore.getIsDynamicAddedRoute) { if (permissionStore.getIsDynamicAddedRoute) {
next(); next();
return; return;
} }
const routes = await permissionStore.buildRoutesAction(); const routes = await permissionStore.buildRoutesAction();
routes.forEach((route) => { routes.forEach((route) => {
......
import type { Router } from 'vue-router';
import { useTransitionSetting } from '/@/hooks/setting/useTransitionSetting';
import nProgress from 'nprogress';
import { unref } from 'vue';
export function createProgressGuard(router: Router) {
const { getOpenNProgress } = useTransitionSetting();
router.beforeEach(async (to) => {
if (to.meta.loaded) return true;
unref(getOpenNProgress) && nProgress.start();
return true;
});
router.afterEach(async () => {
// if (to.meta.loaded) return true;
unref(getOpenNProgress) && nProgress.done();
return true;
});
}
import type { RouteLocationNormalized, Router } from 'vue-router';
const isHash = (href: string) => {
return /^#/.test(href);
};
export function createScrollGuard(router: Router) {
const body = document.body;
router.afterEach(async (to) => {
// scroll top
isHash((to as RouteLocationNormalized & { href: string })?.href) && body.scrollTo(0, 0);
return true;
});
}
...@@ -9,7 +9,7 @@ import { createRouter, createWebHashHistory } from 'vue-router'; ...@@ -9,7 +9,7 @@ import { createRouter, createWebHashHistory } from 'vue-router';
export type LayoutMapKey = 'LAYOUT'; export type LayoutMapKey = 'LAYOUT';
const IFRAME = () => import('/@/views/sys/iframe/FrameBlank.vue'); const IFRAME = () => import('/@/views/sys/iframe/FrameBlank.vue');
const LayoutMap = new Map<String, () => Promise<typeof import('*.vue')>>(); const LayoutMap = new Map<string, () => Promise<typeof import('*.vue')>>();
LayoutMap.set('LAYOUT', LAYOUT); LayoutMap.set('LAYOUT', LAYOUT);
LayoutMap.set('IFRAME', IFRAME); LayoutMap.set('IFRAME', IFRAME);
...@@ -27,7 +27,7 @@ function asyncImportRoute(routes: AppRouteRecordRaw[] | undefined) { ...@@ -27,7 +27,7 @@ function asyncImportRoute(routes: AppRouteRecordRaw[] | undefined) {
const { component, name } = item; const { component, name } = item;
const { children } = item; const { children } = item;
if (component) { if (component) {
const layoutFound = LayoutMap.get(component); const layoutFound = LayoutMap.get(component as string);
if (layoutFound) { if (layoutFound) {
item.component = layoutFound; item.component = layoutFound;
} else { } else {
...@@ -66,10 +66,10 @@ function dynamicImport( ...@@ -66,10 +66,10 @@ function dynamicImport(
// Turn background objects into routing objects // Turn background objects into routing objects
export function transformObjToRoute<T = AppRouteModule>(routeList: AppRouteModule[]): T[] { export function transformObjToRoute<T = AppRouteModule>(routeList: AppRouteModule[]): T[] {
routeList.forEach((route) => { routeList.forEach((route) => {
if (route.component) { const component = route.component as string;
if ((route.component as string).toUpperCase() === 'LAYOUT') { if (component) {
//route.component = LayoutMap.get(route.component as LayoutMapKey); if (component.toUpperCase() === 'LAYOUT') {
route.component = LayoutMap.get((route.component as string).toUpperCase() as LayoutMapKey); route.component = LayoutMap.get(component.toUpperCase());
} else { } else {
route.children = [cloneDeep(route)]; route.children = [cloneDeep(route)];
route.component = LAYOUT; route.component = LAYOUT;
......
import type { App } from 'vue'; import type { App } from 'vue';
import { createPinia } from 'pinia'; import { createPinia } from 'pinia';
const store = createPinia(); const store = createPinia();
export function setupStore(app: App<Element>) { export function setupStore(app: App<Element>) {
......
/** /**
* https://github.com/developit/mitt * copy to https://github.com/developit/mitt
* Expand clear method
*/ */
export type EventType = string | symbol; export type EventType = string | symbol;
......
...@@ -9278,7 +9278,7 @@ pify@^4.0.1: ...@@ -9278,7 +9278,7 @@ pify@^4.0.1:
resolved "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" resolved "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231"
integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==
pinia@2.0.0-beta.3: pinia@^2.0.0-beta.3:
version "2.0.0-beta.3" version "2.0.0-beta.3"
resolved "https://registry.yarnpkg.com/pinia/-/pinia-2.0.0-beta.3.tgz#c6f0d07da54dc5aa237f4cc9281898e927b33d16" resolved "https://registry.yarnpkg.com/pinia/-/pinia-2.0.0-beta.3.tgz#c6f0d07da54dc5aa237f4cc9281898e927b33d16"
integrity sha512-4ygKhe9FrYD69tJ7nSdgHm9Ldb0aM/Nzyb8Qz/RZuzOyOr85jWHNmCAhCytWy0l9C4/ypGJYCEJ3vuZfyWjcZA== integrity sha512-4ygKhe9FrYD69tJ7nSdgHm9Ldb0aM/Nzyb8Qz/RZuzOyOr85jWHNmCAhCytWy0l9C4/ypGJYCEJ3vuZfyWjcZA==
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册