coder-common-thin-frontend/src/utils/component-guard.ts
Leo 5e13342f7b fix(router): 修复Vue Router导航错误和组件生命周期问题
* 新增导航防护机制
  - NavigationGuard类防止快速路由切换导致的错误
  - 实现防抖和安全导航方法(safePush/safeReplace)
* 新增组件安全加载机制
  - safeAsyncComponent包装器处理异步组件加载错误
  - 支持重试机制和ChunkLoadError恢复
* 增强路由守卫错误处理
  - 全面的try-catch错误捕获
  - 统一的路由错误处理函数
* 优化路由配置
  - 使用安全组件加载器包装所有异步组件
  - 改进路由重定向逻辑

解决了"Cannot read properties of null (reading 'isUnmounted')"等Vue Router错误
2025-07-06 02:33:37 +08:00

110 lines
3.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import type { AsyncComponentLoader, Component } from 'vue'
import { defineAsyncComponent } from 'vue'
/**
* 安全的异步组件加载器
* 防止在组件卸载时继续加载导致的内存泄漏和错误
*/
export function safeAsyncComponent(
loader: AsyncComponentLoader,
options?: {
loadingComponent?: Component
errorComponent?: Component
delay?: number
timeout?: number
suspensible?: boolean
onError?: (error: Error, retry: () => void, fail: () => void, attempts: number) => any
},
) {
const safeLoader: AsyncComponentLoader = () => {
return loader().catch((error) => {
console.error('异步组件加载失败:', error)
// 如果是网络错误或者加载错误,返回一个空的组件
if (error.name === 'ChunkLoadError' || error.message?.includes('Loading chunk')) {
console.warn('检测到代码分割加载错误,尝试重新加载页面')
// 延迟重新加载页面,避免无限循环
setTimeout(() => {
window.location.reload()
}, 1000)
}
// 返回一个错误组件
return Promise.resolve({
template: '<div class="error-component">组件加载失败</div>',
})
})
}
return defineAsyncComponent({
loader: safeLoader,
loadingComponent: options?.loadingComponent,
errorComponent: options?.errorComponent,
delay: options?.delay ?? 200,
timeout: options?.timeout ?? 30000,
suspensible: options?.suspensible ?? false,
onError: options?.onError || ((error, retry, fail, attempts) => {
console.error(`异步组件加载错误 (第${attempts}次尝试):`, error)
if (attempts <= 3) {
retry()
}
else {
fail()
}
}),
})
}
/**
* 创建路由组件的安全加载器
*/
export function createSafeRouteComponent(componentPath: string) {
return safeAsyncComponent(
() => import(/* @vite-ignore */ `/src/views${componentPath}.vue`),
{
delay: 100,
timeout: 10000,
onError: (error, retry, fail, attempts) => {
console.error(`路由组件加载失败: ${componentPath}`, error)
// 对于路由组件最多重试2次
if (attempts <= 2) {
console.warn(`重试加载组件: ${componentPath} (第${attempts}次)`)
retry()
}
else {
console.error(`组件加载最终失败: ${componentPath}`)
fail()
}
},
},
)
}
/**
* 清理组件缓存,用于解决热更新时的问题
*/
export function clearComponentCache() {
// 在开发环境下清理模块缓存
if (import.meta.hot) {
import.meta.hot.invalidate()
}
}
/**
* 组件安全性检查
*/
export function validateComponent(component: any): boolean {
if (!component) {
console.error('组件为空或未定义')
return false
}
if (typeof component !== 'object' && typeof component !== 'function') {
console.error('组件类型不正确:', typeof component)
return false
}
return true
}