perf: 页面懒加载交互体验优化
This commit is contained in:
@@ -15,6 +15,8 @@ interface NetworkConnectionLike {
|
|||||||
saveData?: boolean;
|
saveData?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const CHUNK_ERROR_RELOAD_KEY = '__easyflow_chunk_error_reload_path__';
|
||||||
|
|
||||||
function isSlowNetworkConnection() {
|
function isSlowNetworkConnection() {
|
||||||
if (typeof navigator === 'undefined') {
|
if (typeof navigator === 'undefined') {
|
||||||
return false;
|
return false;
|
||||||
@@ -35,6 +37,46 @@ function isSlowNetworkConnection() {
|
|||||||
return ['2g', '3g', 'slow-2g'].includes(connection.effectiveType ?? '');
|
return ['2g', '3g', 'slow-2g'].includes(connection.effectiveType ?? '');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isDynamicImportChunkError(error: unknown) {
|
||||||
|
const message = String((error as Error | undefined)?.message ?? error ?? '');
|
||||||
|
if (!message) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
/Loading chunk/i.test(message) ||
|
||||||
|
/Importing a module script failed/i.test(message) ||
|
||||||
|
/Failed to fetch dynamically imported module/i.test(message) ||
|
||||||
|
/dynamically imported module/i.test(message)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setupChunkErrorGuard(router: Router) {
|
||||||
|
router.onError((error, to) => {
|
||||||
|
if (!isDynamicImportChunkError(error)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const fullPath = to?.fullPath;
|
||||||
|
if (!fullPath) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const lastReloadPath = sessionStorage.getItem(CHUNK_ERROR_RELOAD_KEY);
|
||||||
|
if (lastReloadPath === fullPath) {
|
||||||
|
sessionStorage.removeItem(CHUNK_ERROR_RELOAD_KEY);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sessionStorage.setItem(CHUNK_ERROR_RELOAD_KEY, fullPath);
|
||||||
|
window.location.assign(fullPath);
|
||||||
|
});
|
||||||
|
|
||||||
|
router.isReady().finally(() => {
|
||||||
|
const reloadedPath = sessionStorage.getItem(CHUNK_ERROR_RELOAD_KEY);
|
||||||
|
if (reloadedPath && router.currentRoute.value.fullPath === reloadedPath) {
|
||||||
|
sessionStorage.removeItem(CHUNK_ERROR_RELOAD_KEY);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function shouldUseRouteProgress() {
|
function shouldUseRouteProgress() {
|
||||||
if (preferences.transition.progress) {
|
if (preferences.transition.progress) {
|
||||||
return true;
|
return true;
|
||||||
@@ -61,10 +103,11 @@ function setupCommonGuard(router: Router) {
|
|||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
router.afterEach((to) => {
|
router.afterEach((to, _from, failure) => {
|
||||||
// 记录页面是否加载,如果已经加载,后续的页面切换动画等效果不在重复执行
|
// 记录页面是否加载,如果已经加载,后续的页面切换动画等效果不在重复执行
|
||||||
|
if (!failure) {
|
||||||
loadedPaths.add(to.path);
|
loadedPaths.add(to.path);
|
||||||
|
}
|
||||||
|
|
||||||
// 关闭页面加载进度条
|
// 关闭页面加载进度条
|
||||||
if (shouldUseRouteProgress()) {
|
if (shouldUseRouteProgress()) {
|
||||||
@@ -161,6 +204,8 @@ function createRouterGuard(router: Router) {
|
|||||||
setupCommonGuard(router);
|
setupCommonGuard(router);
|
||||||
/** 权限访问 */
|
/** 权限访问 */
|
||||||
setupAccessGuard(router);
|
setupAccessGuard(router);
|
||||||
|
/** chunk加载错误兜底 */
|
||||||
|
setupChunkErrorGuard(router);
|
||||||
}
|
}
|
||||||
|
|
||||||
export { createRouterGuard };
|
export { createRouterGuard };
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type {VNode} from 'vue';
|
import type {VNode} from 'vue';
|
||||||
|
import {computed} from 'vue';
|
||||||
import type {
|
import type {
|
||||||
RouteLocationNormalizedLoaded,
|
RouteLocationNormalizedLoaded,
|
||||||
RouteLocationNormalizedLoadedGeneric,
|
RouteLocationNormalizedLoadedGeneric,
|
||||||
} from 'vue-router';
|
} from 'vue-router';
|
||||||
|
|
||||||
import { computed } from 'vue';
|
|
||||||
import {RouterView} from 'vue-router';
|
import {RouterView} from 'vue-router';
|
||||||
|
|
||||||
import {preferences, usePreferences} from '@easyflow/preferences';
|
import {preferences, usePreferences} from '@easyflow/preferences';
|
||||||
@@ -104,7 +103,6 @@ function transformComponent(
|
|||||||
v-if="getEnabledTransition"
|
v-if="getEnabledTransition"
|
||||||
:name="getTransitionName(route)"
|
:name="getTransitionName(route)"
|
||||||
appear
|
appear
|
||||||
mode="out-in"
|
|
||||||
>
|
>
|
||||||
<KeepAlive
|
<KeepAlive
|
||||||
v-if="keepAlive"
|
v-if="keepAlive"
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import { findRootMenuByPath } from '@easyflow/utils';
|
|||||||
import {useNavigation} from './use-navigation';
|
import {useNavigation} from './use-navigation';
|
||||||
|
|
||||||
function useMixedMenu() {
|
function useMixedMenu() {
|
||||||
const { navigation, willOpenedByWindow } = useNavigation();
|
const { navigation, prefetch, willOpenedByWindow } = useNavigation();
|
||||||
const accessStore = useAccessStore();
|
const accessStore = useAccessStore();
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const splitSideMenus = ref<MenuRecordRaw[]>([]);
|
const splitSideMenus = ref<MenuRecordRaw[]>([]);
|
||||||
@@ -85,6 +85,8 @@ function useMixedMenu() {
|
|||||||
* @param mode 菜单模式
|
* @param mode 菜单模式
|
||||||
*/
|
*/
|
||||||
const handleMenuSelect = (key: string, mode?: string) => {
|
const handleMenuSelect = (key: string, mode?: string) => {
|
||||||
|
prefetch(key);
|
||||||
|
|
||||||
if (!needSplit.value || mode === 'vertical') {
|
if (!needSplit.value || mode === 'vertical') {
|
||||||
navigation(key);
|
navigation(key);
|
||||||
return;
|
return;
|
||||||
@@ -95,6 +97,11 @@ function useMixedMenu() {
|
|||||||
if (!willOpenedByWindow(key)) {
|
if (!willOpenedByWindow(key)) {
|
||||||
rootMenuPath.value = rootMenu?.path ?? '';
|
rootMenuPath.value = rootMenu?.path ?? '';
|
||||||
splitSideMenus.value = _splitSideMenus;
|
splitSideMenus.value = _splitSideMenus;
|
||||||
|
_splitSideMenus.forEach((menu) => {
|
||||||
|
if (menu.path) {
|
||||||
|
prefetch(menu.path);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_splitSideMenus.length === 0) {
|
if (_splitSideMenus.length === 0) {
|
||||||
@@ -114,6 +121,8 @@ function useMixedMenu() {
|
|||||||
* @param parentsPath 父级路径
|
* @param parentsPath 父级路径
|
||||||
*/
|
*/
|
||||||
const handleMenuOpen = (key: string, parentsPath: string[]) => {
|
const handleMenuOpen = (key: string, parentsPath: string[]) => {
|
||||||
|
prefetch(key);
|
||||||
|
|
||||||
if (parentsPath.length <= 1 && preferences.sidebar.autoActivateChild) {
|
if (parentsPath.length <= 1 && preferences.sidebar.autoActivateChild) {
|
||||||
navigation(
|
navigation(
|
||||||
defaultSubMap.has(key) ? (defaultSubMap.get(key) as string) : key,
|
defaultSubMap.has(key) ? (defaultSubMap.get(key) as string) : key,
|
||||||
|
|||||||
Reference in New Issue
Block a user