@gulibs/react-autoroutes-client
v0.0.18
Published
Client-side utilities for React auto routes
Maintainers
Readme
@gulibs/react-autoroutes-client
一个功能强大的 React 路由自动化客户端库,提供完整的路由保护、状态管理、国际化和性能优化解决方案。
✨ 特性
- 🛡️ 路由保护 - 支持认证、权限、角色和自定义守卫
- 🏗️ 中间件系统 - 可插拔的中间件架构
- 🌍 国际化支持 - 与 @gulibs/vite-plugin-i18n 完美集成
- 📊 状态管理 - 基于 React Storage 的用户认证状态管理
- ⚡ 性能优化 - 内置缓存、防抖和性能监控
- 🎯 TypeScript - 完整的类型定义支持
- 📱 现代化 - 支持 React 18+ 和现代浏览器
- 🔄 热更新 - 支持开发时的热模块替换
- 📦 模块化 - 按需导入,减小包大小
📦 安装
npm install @gulibs/react-autoroutes-client
# 或
pnpm add @gulibs/react-autoroutes-client
# 或
yarn add @gulibs/react-autoroutes-client对等依赖
npm install react react-dom react-router lodash🚀 快速开始
1. 路由保护
import { RouteProtectionWrapper, defineAuth, defineGuard } from '@gulibs/react-autoroutes-client';
// 定义认证守卫
const authGuard = defineAuth({
redirectTo: '/login',
errorMessage: '请先登录'
});
// 定义自定义守卫
const adminGuard = defineGuard({
name: 'admin-only',
condition: (context) => context.user?.role === 'admin',
redirectTo: '/forbidden',
errorMessage: '需要管理员权限'
});
function ProtectedPage() {
return (
<RouteProtectionWrapper
guards={[authGuard, adminGuard]}
loadingElement={<div>验证中...</div>}
component={<YourPageComponent />}
/>
);
}2. 用户状态管理
import { useUser } from '@gulibs/react-autoroutes-client';
function AuthComponent() {
const {
user,
isAuthenticated,
isLoading,
login,
logout,
error
} = useUser({
loginApi: async (credentials) => {
const response = await fetch('/api/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(credentials)
});
return response.json();
},
fetchUser: async (token) => {
const response = await fetch('/api/user', {
headers: { Authorization: `Bearer ${token}` }
});
return response.json();
}
});
if (isLoading) return <div>加载中...</div>;
if (error) return <div>错误: {error}</div>;
return (
<div>
{isAuthenticated ? (
<div>
<p>欢迎, {user?.name}</p>
<button onClick={logout}>退出登录</button>
</div>
) : (
<button onClick={() => login({ username: 'demo', password: '123' })}>
登录
</button>
)}
</div>
);
}3. 国际化支持
import { I18nProvider, useI18n, createI18nClient } from '@gulibs/react-autoroutes-client';
// 创建国际化客户端
const i18nClient = createI18nClient({
defaultLocale: 'zh',
supportedLocales: ['zh', 'en'],
resources: {
zh: {
welcome: '欢迎',
hello: '你好, {name}!'
},
en: {
welcome: 'Welcome',
hello: 'Hello, {name}!'
}
}
});
function App() {
return (
<I18nProvider client={i18nClient}>
<MyComponent />
</I18nProvider>
);
}
function MyComponent() {
const { t, locale, setLocale } = useI18n();
return (
<div>
<p>{t('welcome')}</p>
<p>{t('hello', { name: '张三' })}</p>
<button onClick={() => setLocale(locale === 'zh' ? 'en' : 'zh')}>
切换语言
</button>
</div>
);
}4. 页面配置和 Hooks
import {
useHandle,
usePageConfig,
useI18nPageConfig,
useVGroveLayoutSettings,
useLayoutFeatures,
defineHandle
} from '@gulibs/react-autoroutes-client';
// 定义页面处理配置
export const handle = defineHandle({
meta: {
title: 'page.title',
description: '页面描述'
},
breadcrumbs: {
href: '/current-page',
children: '当前页面'
},
layoutSettings: {
variant: 'modern',
sidebar: true,
header: true,
footer: false,
content: {
maxWidth: '1400px',
padding: '2rem',
centered: true,
fullWidth: false
},
navbar: {
sticky: true,
transparent: false,
height: '72px'
},
sidebarConfig: {
position: 'left',
width: '320px',
collapsible: true,
defaultCollapsed: false
},
theme: {
mode: 'dark',
primaryColor: '#3b82f6'
},
responsive: {
hideSidebarOnMobile: true,
mobileBreakpoint: '768px'
}
}
});
function MyPage() {
// 获取页面配置
const { meta, breadcrumbs, layoutSettings } = usePageConfig(handle);
// 获取 VGrove 布局配置(带默认值)
const vgroveLayout = useVGroveLayoutSettings(handle);
// 获取布局功能状态
const {
hasSidebar,
hasHeader,
hasFooter,
isModern,
isSidebarCollapsible,
shouldHideSidebarOnMobile
} = useLayoutFeatures(handle);
// 获取国际化页面配置
const i18nConfig = useI18nPageConfig(handle);
return (
<div>
<h1>{meta?.title}</h1>
{hasSidebar && <div>显示侧边栏</div>}
{hasHeader && <div>显示头部</div>}
{/* 页面内容 */}
</div>
);
}🎯 高级用法
中间件系统
import { defineMiddleware } from '@gulibs/react-autoroutes-client';
const loggingMiddleware = defineMiddleware({
name: 'logging',
priority: 10,
handler: async (context, next) => {
console.log(`访问页面: ${context.path}`);
const start = Date.now();
await next();
const duration = Date.now() - start;
console.log(`页面处理耗时: ${duration}ms`);
}
});
// 全局性能监控中间件
const performanceMiddleware = defineMiddleware({
name: 'performance',
priority: 1,
handler: async (context, next) => {
if (window.performance) {
const mark = `route-${context.path}-start`;
performance.mark(mark);
}
await next();
if (window.performance) {
const endMark = `route-${context.path}-end`;
performance.mark(endMark);
performance.measure(`route-${context.path}`, `route-${context.path}-start`, endMark);
}
}
});
<RouteProtectionWrapper
middlewares={[performanceMiddleware, loggingMiddleware]}
component={<YourComponent />}
/>国际化资源加载器
import { createI18nLoader, ViteI18nLoader, createI18nClient } from '@gulibs/react-autoroutes-client';
// 1. 基础资源加载器
const loader = createI18nLoader({
basePath: '/api/locales',
extensions: ['.json'],
cache: true,
debug: true
});
// 2. 自定义获取函数
const customLoader = createI18nLoader({
fetchResources: async (locale, path) => {
const response = await fetch(`/api/i18n/${locale}`);
if (!response.ok) {
throw new Error(`Failed to load locale: ${response.statusText}`);
}
return response.json();
},
onLoad: (locale, data) => {
console.log(`✅ Loaded ${locale}:`, Object.keys(data));
},
onError: (locale, error) => {
console.error(`❌ Failed to load ${locale}:`, error);
}
});
// 3. 高级加载器实例
const advancedLoader = new ViteI18nLoader({
basePath: '/locales',
cache: true,
cacheTime: 30 * 60 * 1000, // 30分钟
debug: process.env.NODE_ENV === 'development'
});
// 4. 与国际化客户端集成
const i18nClient = createI18nClient({
defaultLocale: 'zh',
supportedLocales: ['zh', 'en'],
basePath: '/api/locales',
cache: true,
detectBrowserLanguage: true,
persistence: {
enabled: true,
key: 'user_locale',
storage: 'localStorage'
}
});
// 5. 手动预加载资源
await loader.preloadResources(['zh', 'en']);
// 6. 检查加载器状态
console.log('Available keys:', loader.getAvailableKeys?.());
console.log('Has key "welcome":', loader.hasKey?.('welcome'));
console.log('Using virtual module:', advancedLoader.isUsingVirtualModule?.());复杂路由守卫
// 角色守卫
const roleGuard = defineAuth({
roles: ['admin', 'moderator'],
redirectTo: '/login',
errorMessage: '需要以下角色之一: admin, moderator'
});
// 权限守卫
const permissionGuard = defineGuard({
name: 'permission-user:read,user:write',
condition: (context) => {
return context.user?.permissions?.includes('user:read') &&
context.user?.permissions?.includes('user:write');
},
redirectTo: '/forbidden',
errorMessage: '需要以下权限: user:read, user:write'
});
// 自定义复合守卫
const complexGuard = defineGuard({
name: 'complex-auth',
condition: async (context) => {
// 检查用户是否已认证
if (!context.user) return false;
// 检查时间限制
const now = new Date();
const workingHours = now.getHours() >= 9 && now.getHours() <= 18;
// 检查用户类型和时间
return context.user.type === 'employee' ? workingHours : true;
},
redirectTo: '/access-denied',
errorMessage: '当前时间段无法访问'
});
// 环境守卫
const environmentGuard = defineGuard({
name: 'environment',
condition: () => {
return process.env.NODE_ENV === 'development' ||
localStorage.getItem('feature_flag_enabled') === 'true';
},
redirectTo: '/not-available'
});性能优化工具
import {
PerformanceCache,
BatchProcessor,
PerformanceTracker,
MemoryOptimizer
} from '@gulibs/react-autoroutes-client';
// 创建缓存实例
const cache = new PerformanceCache<string, any>(100);
// 批处理器
const processor = new BatchProcessor(5, 100);
// 性能追踪
const tracker = new PerformanceTracker();
// 内存优化器
const memoryOptimizer = MemoryOptimizer.getInstance();
function useOptimizedData() {
useEffect(() => {
// 启动性能追踪
tracker.startTimer('data-loading');
// 批处理数据加载
const loadData = async () => {
const items = ['user', 'settings', 'permissions'];
const results = await processor.processBatch(
items,
async (item) => {
const cached = cache.get(item);
if (cached) return cached;
const data = await fetchData(item);
cache.set(item, data);
return data;
}
);
return results;
};
loadData().finally(() => {
const duration = tracker.endTimer('data-loading');
console.log(`数据加载耗时: ${duration}ms`);
});
// 注册清理任务
memoryOptimizer.addCleanupTask(() => {
cache.clear();
});
return () => {
memoryOptimizer.runCleanup();
};
}, []);
}工具函数和实用程序
import {
normalizePath,
parseQuery,
buildQuery,
debounce,
throttle,
deepMerge,
safeParseInt,
isEmpty,
measureTime
} from '@gulibs/react-autoroutes-client';
// 路径处理
const normalizedPath = normalizePath('//path//to//page//');
// => '/path/to/page'
// 查询参数处理
const params = parseQuery('?name=john&age=25&active=true');
// => { name: 'john', age: '25', active: 'true' }
const query = buildQuery({ name: 'jane', age: 30 });
// => 'name=jane&age=30'
// 防抖和节流
const debouncedSearch = debounce((query: string) => {
searchAPI(query);
}, 500);
const throttledScroll = throttle(() => {
updateScrollPosition();
}, 100);
// 性能测量
const timedFunction = measureTime(expensiveFunction, 'expensive-operation');
// 数据处理
const config = deepMerge(defaultConfig, userConfig);
const count = safeParseInt(userInput, 0);
const hasData = !isEmpty(responseData);📖 API 参考
路由保护
RouteProtectionWrapper
路由保护包装器组件。
Props:
interface RouteProtectionWrapperProps {
guards?: (AuthOptions | GuardOptions | Function)[];
middlewares?: (MiddlewareOptions | Function)[];
loadingElement?: React.ReactNode;
component: React.ReactNode;
children?: React.ReactNode;
}defineAuth(options: AuthOptions)
定义认证守卫。
interface AuthOptions {
name?: string;
redirectTo?: string;
errorMessage?: string;
check?: (context: AuthContext) => boolean | Promise<boolean>;
roles?: string[];
permissions?: string[];
}defineGuard(options: GuardOptions)
定义自定义守卫。
interface GuardOptions {
name?: string;
type?: 'auth' | 'role' | 'permission' | 'custom';
redirectTo?: string;
errorMessage?: string;
condition: (context: GuardContext) => boolean | Promise<boolean>;
}defineMiddleware(options: MiddlewareOptions)
定义中间件。
interface MiddlewareOptions {
name?: string;
priority?: number;
devOnly?: boolean;
handler: (context: MiddlewareContext, next: () => Promise<void> | void) => Promise<void> | void;
}状态管理
useUser<TUser, TCredentials>(options: UseUserOptions)
用户状态管理 Hook。
返回值:
interface AuthState<TUser> & AuthActions<TUser, TCredentials> {
user: TUser | null;
token: string | null;
refreshToken: string | null;
isAuthenticated: boolean;
isLoading: boolean;
error: string | null;
login: (credentials: TCredentials) => Promise<void>;
logout: () => void;
refreshUser: () => Promise<void>;
refreshTokenAction: () => Promise<void>;
updateUser: (userData: Partial<TUser>) => void;
setToken: (token: string) => void;
setRefreshToken: (refreshToken: string) => void;
clearError: () => void;
}useStorage<T>(key: string, defaultValue: T, options?)
通用存储 Hook。
function useStorage<T>(
key: string,
defaultValue: T,
options?: { storage?: 'local' | 'session' }
): [T, (value: T) => void, () => void]国际化
I18nProvider
国际化提供者组件。
interface I18nProviderProps {
children: ReactNode;
client?: I18nClient | I18nClientConfig;
defaultLocale?: string;
locales?: string[];
loadResources?: (locale: string) => Promise<Record<string, any>>;
resources?: Record<string, Record<string, any>>;
}useI18n()
国际化 Hook。
interface I18nContextType {
locale: string;
setLocale: (locale: string) => Promise<void>;
t: (key: string, params?: Record<string, any>) => string;
isReady: boolean;
availableLocales: string[];
availableKeys: string[];
hasKey: (key: string) => boolean;
getNamespaceResources: (namespace: string) => Record<string, any> | undefined;
isUsingVirtualModule: boolean;
}createI18nClient(config: I18nClientConfig)
创建国际化客户端。
interface I18nClientConfig extends I18nLoaderConfig {
defaultLocale: string;
supportedLocales?: string[];
resources?: Record<string, Record<string, any>>;
fallbackToDefault?: boolean;
detectBrowserLanguage?: boolean;
persistence?: {
enabled?: boolean;
key?: string;
storage?: 'localStorage' | 'sessionStorage';
};
interpolation?: {
prefix?: string;
suffix?: string;
escape?: (value: any) => string;
};
}createI18nLoader(config: I18nLoaderConfig)
创建资源加载器。
interface I18nLoaderConfig {
basePath?: string;
extensions?: string[];
localePattern?: 'directory' | 'filename';
defaultLocale?: string;
cacheTime?: number;
cache?: boolean;
debug?: boolean;
onLoad?: (locale: string, data: Record<string, any>) => void;
onError?: (locale: string, error: Error) => void;
fetchResources?: (locale: string, path: string) => Promise<Record<string, any>>;
}页面配置
usePageConfig<Keys>(handle?, options?)
获取页面配置。
useI18nPageConfig<Keys>(handle?)
获取国际化页面配置。
useHandle<Keys>()
获取当前页面的 handle。
useBreadcrumbs<Keys>(handle?, options?)
获取面包屑配置。
useDocumentTitle<Keys>(handle?, suffix?, options?)
设置文档标题。
useVGroveLayoutSettings<Keys>(handle?, defaults?)
获取 VGrove 布局配置,带默认值处理。
function useVGroveLayoutSettings<Keys extends string = string>(
handle?: PageHandle<Keys>,
defaults?: Partial<LayoutSettings>
): LayoutSettingsuseLayoutFeatures<Keys>(handle?)
检查布局功能是否启用。
interface LayoutFeatures {
hasSidebar: boolean;
hasHeader: boolean;
hasFooter: boolean;
isCompact: boolean;
isModern: boolean;
isMinimal: boolean;
isSidebarCollapsible: boolean;
isNavbarSticky: boolean;
isFooterSticky: boolean;
shouldHideSidebarOnMobile: boolean;
}useLayoutConfig<Keys>(section, handle?)
获取布局配置的特定部分。
function useLayoutConfig<Keys extends string = string>(
section: keyof LayoutSettings,
handle?: PageHandle<Keys>
): anyLayoutSettings 接口
VGrove 布局配置接口,支持完整的布局定制。
interface LayoutSettings {
/** 布局变体 */
variant?: 'default' | 'compact' | 'modern' | 'minimal' | string;
/** 是否显示侧边栏 */
sidebar?: boolean;
/** 是否显示导航栏/头部 */
header?: boolean;
/** 是否显示页脚 */
footer?: boolean;
/** 内容区域配置 */
content?: {
/** 内容最大宽度 */
maxWidth?: string;
/** 内容内边距 */
padding?: string;
/** 是否居中对齐 */
centered?: boolean;
/** 是否全宽显示 */
fullWidth?: boolean;
};
/** 导航栏配置 */
navbar?: {
/** 是否固定在顶部 */
sticky?: boolean;
/** 背景透明度 */
transparent?: boolean;
/** 高度 */
height?: string;
};
/** 侧边栏配置 */
sidebarConfig?: {
/** 侧边栏位置 */
position?: 'left' | 'right';
/** 侧边栏宽度 */
width?: string;
/** 是否可折叠 */
collapsible?: boolean;
/** 默认是否折叠 */
defaultCollapsed?: boolean;
};
/** 页脚配置 */
footerConfig?: {
/** 是否固定在底部 */
sticky?: boolean;
/** 高度 */
height?: string;
};
/** 主题相关配置 */
theme?: {
/** 主题模式 */
mode?: 'light' | 'dark' | 'auto';
/** 主色调 */
primaryColor?: string;
};
/** 响应式配置 */
responsive?: {
/** 移动端是否隐藏侧边栏 */
hideSidebarOnMobile?: boolean;
/** 移动端断点 */
mobileBreakpoint?: string;
};
}性能优化
PerformanceCache<K, V>
高性能 LRU 缓存。
class PerformanceCache<K, V> {
constructor(maxSize: number = 100);
get(key: K): V | undefined;
set(key: K, value: V): void;
has(key: K): boolean;
delete(key: K): boolean;
clear(): void;
size(): number;
}BatchProcessor
批处理器。
class BatchProcessor {
constructor(batchSize: number = 10, delay: number = 50);
async processBatch<T, R>(
items: T[],
processor: (item: T) => Promise<R>
): Promise<R[]>;
}PerformanceTracker
性能追踪器。
class PerformanceTracker {
startTimer(name: string): void;
endTimer(name: string): number;
increment(name: string): void;
getCounter(name: string): number;
reset(): void;
getStats(): Record<string, any>;
}🏆 最佳实践
1. VGrove 布局配置最佳实践
// layout/LayoutProvider.tsx
import { useVGroveLayoutSettings, useLayoutFeatures } from '@gulibs/react-autoroutes-client';
import { VGroveLayout } from '@gulibs/vgrove-ui';
export function LayoutProvider({ children, handle }) {
// 获取完整的布局配置
const layoutConfig = useVGroveLayoutSettings(handle, {
// 全局默认配置
variant: 'modern',
content: {
maxWidth: '1400px',
padding: '1.5rem'
},
theme: {
mode: 'light'
}
});
// 获取布局功能状态
const features = useLayoutFeatures(handle);
return (
<VGroveLayout
config={layoutConfig}
features={features}
>
{children}
</VGroveLayout>
);
}
// pages/dashboard/handle.ts
import { defineHandle } from '@gulibs/react-autoroutes-client';
export const handle = defineHandle({
meta: {
title: 'dashboard.title',
description: 'dashboard.description'
},
layoutSettings: {
variant: 'modern',
sidebar: true,
header: true,
footer: false,
content: {
maxWidth: '100%',
fullWidth: true,
padding: '0'
},
sidebarConfig: {
width: '280px',
collapsible: true,
defaultCollapsed: false
},
navbar: {
sticky: true,
height: '64px'
},
responsive: {
hideSidebarOnMobile: true
}
}
});
// pages/dashboard/page.tsx
import { useVGroveLayoutSettings } from '@gulibs/react-autoroutes-client';
import { handle } from './handle';
export default function DashboardPage() {
const layoutConfig = useVGroveLayoutSettings(handle);
return (
<div className="dashboard">
<h1>仪表板</h1>
{/* 根据布局配置显示内容 */}
</div>
);
}
// pages/settings/handle.ts
export const handle = defineHandle({
meta: {
title: 'settings.title'
},
layoutSettings: {
variant: 'compact',
sidebar: false,
header: true,
footer: true,
content: {
maxWidth: '800px',
centered: true,
padding: '2rem'
}
}
});2. 路由守卫组织
// guards/index.ts
export const authGuard = defineAuth({
redirectTo: '/login',
errorMessage: '请先登录'
});
export const adminGuard = defineAuth({
roles: ['admin'],
redirectTo: '/forbidden',
errorMessage: '需要管理员权限'
});
export const premiumGuard = defineGuard({
name: 'premium',
condition: (context) => context.user?.isPremium,
redirectTo: '/upgrade',
errorMessage: '需要升级到高级版'
});
// guards/factory.ts
export function createRoleGuard(roles: string[], redirectTo = '/forbidden') {
return defineAuth({
roles,
redirectTo,
errorMessage: `需要以下角色之一: ${roles.join(', ')}`
});
}
export function createPermissionGuard(permissions: string[], redirectTo = '/forbidden') {
return defineGuard({
name: `permission-${permissions.join('-')}`,
condition: (context) => {
return permissions.every(permission =>
context.user?.permissions?.includes(permission)
);
},
redirectTo,
errorMessage: `需要以下权限: ${permissions.join(', ')}`
});
}3. 国际化最佳实践
// i18n/setup.ts
import { createI18nClient } from '@gulibs/react-autoroutes-client';
export const i18nClient = createI18nClient({
defaultLocale: 'zh',
supportedLocales: ['zh', 'en'],
fallbackToDefault: true,
detectBrowserLanguage: true,
persistence: {
enabled: true,
key: 'app_locale',
storage: 'localStorage'
},
interpolation: {
prefix: '{',
suffix: '}',
escape: (value) => String(value).replace(/[<>]/g, '')
},
debug: process.env.NODE_ENV === 'development',
// 开发环境短缓存,生产环境长缓存
cacheTime: process.env.NODE_ENV === 'production'
? 24 * 60 * 60 * 1000
: 5 * 60 * 1000,
onError: (locale, error) => {
// 发送错误到监控系统
console.error(`Failed to load locale ${locale}:`, error);
// 可以集成 Sentry 等错误监控
},
onLoad: (locale, data) => {
console.log(`✅ Loaded ${locale} with ${Object.keys(data).length} keys`);
}
});
// hooks/useI18nHelpers.ts
export function useI18nHelpers() {
const { hasKey, validateKey } = useI18nKeyValidator();
const plural = usePlural();
const translateLocalized = useTranslateLocalized();
return {
hasKey,
validateKey,
plural,
translateLocalized,
// 安全翻译函数,键不存在时返回键名
safeT: (key: string, params?: Record<string, any>) => {
const { t } = useI18n();
return hasKey(key) ? t(key, params) : key;
}
};
}3. 性能优化策略
// utils/performance.ts
import {
PerformanceCache,
BatchProcessor,
MemoryOptimizer
} from '@gulibs/react-autoroutes-client';
// 全局缓存实例
export const globalCache = new PerformanceCache<string, any>(200);
export const batchProcessor = new BatchProcessor(10, 100);
export const memoryOptimizer = MemoryOptimizer.getInstance();
// 启动内存优化
memoryOptimizer.startPeriodicCleanup(5 * 60 * 1000); // 5分钟清理一次
// hooks/useOptimizedApi.ts
export function useOptimizedApi() {
const cache = useMemo(() => new PerformanceCache<string, any>(50), []);
const fetchWithCache = useCallback(async (url: string) => {
const cached = cache.get(url);
if (cached) return cached;
const response = await fetch(url);
const data = await response.json();
cache.set(url, data);
return data;
}, [cache]);
const batchFetch = useCallback(async (urls: string[]) => {
return batchProcessor.processBatch(urls, fetchWithCache);
}, [fetchWithCache]);
useEffect(() => {
// 注册清理任务
memoryOptimizer.addCleanupTask(() => {
cache.clear();
});
}, [cache]);
return { fetchWithCache, batchFetch };
}4. 错误处理
// utils/errorBoundary.tsx
import { defineGuard } from '@gulibs/react-autoroutes-client';
export const errorHandlingGuard = defineGuard({
name: 'error-handling',
condition: async (context) => {
try {
// 检查用户权限
if (!context.user) return false;
// 检查网络状态
if (!navigator.onLine) {
throw new Error('网络连接已断开');
}
return true;
} catch (error) {
// 发送错误到监控系统
console.error('Guard error:', error);
return false;
}
},
redirectTo: '/error'
});
// components/ErrorBoundary.tsx
class ErrorBoundary extends React.Component<
{ children: React.ReactNode },
{ hasError: boolean; error?: Error }
> {
constructor(props: any) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error: Error) {
return { hasError: true, error };
}
componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
console.error('ErrorBoundary caught an error:', error, errorInfo);
// 发送错误到监控系统
}
render() {
if (this.state.hasError) {
return (
<div className="error-fallback">
<h2>出现了一些问题</h2>
<details style={{ whiteSpace: 'pre-wrap' }}>
{this.state.error?.message}
</details>
</div>
);
}
return this.props.children;
}
}🚨 故障排除
常见问题
1. 虚拟模块加载失败
症状: 错误信息 Failed to resolve import "~i18n-locales-loader"
解决方案:
# 检查插件配置
# vite.config.ts 中确保有:
import i18nPlugin from '@gulibs/vite-plugin-i18n';
export default defineConfig({
plugins: [
i18nPlugin({
basePath: 'src/locales',
keysOutput: 'src/i18n-keys.ts'
})
]
});
# 重启开发服务器
npm run dev2. 认证状态不同步
症状: 页面刷新后用户状态丢失
解决方案:
// 确保正确配置 useUser
const { user, isAuthenticated } = useUser({
storagePrefix: 'myapp', // 使用唯一前缀
useSessionStorage: false, // 使用 localStorage 持久化
autoRefreshInterval: 15 * 60 * 1000, // 15分钟自动刷新
});
// 检查 localStorage
console.log('Stored token:', localStorage.getItem('myapp_token'));
console.log('Stored user:', localStorage.getItem('myapp_user'));3. 路由保护不生效
症状: 守卫没有阻止未授权访问
解决方案:
// 检查 RouteProtectionWrapper 的使用
<RouteProtectionWrapper
guards={[authGuard]} // 确保守卫已正确定义
loadingElement={<div>验证中...</div>} // 提供加载状态
component={<ProtectedComponent />}
>
{/* 不要在这里放置组件 */}
</RouteProtectionWrapper>
// 确保守卫返回布尔值
const authGuard = defineAuth({
check: (context) => {
console.log('Auth check:', context.user); // 调试日志
return !!context.user; // 明确返回布尔值
}
});4. 资源加载失败
症状: 国际化资源无法加载
解决方案:
// 启用调试模式
const loader = createI18nLoader({
debug: true, // 查看详细加载日志
onError: (locale, error) => {
console.error(`Loading ${locale} failed:`, error);
// 检查网络请求是否成功
}
});
// 检查资源路径
// 确保服务器上存在对应文件
fetch('/locales/zh.json')
.then(res => res.json())
.then(data => console.log('Resource loaded:', data))
.catch(err => console.error('Resource load failed:', err));
// 检查 CORS 设置(如果是跨域请求)5. 性能问题
症状: 页面加载缓慢
解决方案:
// 启用缓存
const cache = new PerformanceCache<string, any>(100);
// 预加载关键资源
useEffect(() => {
// 预加载用户常用语言
loader.preloadResources(['zh', 'en']);
// 预加载关键数据
batchProcessor.processBatch(
['user', 'settings', 'permissions'],
async (item) => await fetchData(item)
);
}, []);
// 使用 React.memo 优化组件
const OptimizedComponent = React.memo(function MyComponent(props) {
// 组件实现
});
// 使用 useMemo 缓存计算结果
const expensiveValue = useMemo(() => {
return heavyComputation(data);
}, [data]);6. TypeScript 类型错误
症状: TypeScript 编译错误
解决方案:
// 确保导入正确的类型
import type {
AuthOptions,
GuardOptions,
I18nClientConfig
} from '@gulibs/react-autoroutes-client';
// 为用户数据定义接口
interface User {
id: string;
name: string;
role: string;
permissions: string[];
}
// 使用泛型
const { user } = useUser<User, LoginCredentials>({
// 配置...
});
// 确保 tsconfig.json 包含正确设置
{
"compilerOptions": {
"moduleResolution": "node",
"esModuleInterop": true,
"allowSyntheticDefaultImports": true
}
}调试工具
// 开发工具组件
function DevTools() {
const { locale, availableKeys, isUsingVirtualModule } = useI18n();
const { user, isAuthenticated } = useUser();
if (process.env.NODE_ENV !== 'development') return null;
return (
<div style={{ position: 'fixed', top: 0, right: 0, background: '#f0f0f0', padding: '10px' }}>
<h4>调试信息</h4>
<p>当前语言: {locale}</p>
<p>翻译键数量: {availableKeys.length}</p>
<p>使用虚拟模块: {isUsingVirtualModule ? '是' : '否'}</p>
<p>用户认证: {isAuthenticated ? '已登录' : '未登录'}</p>
<p>用户信息: {JSON.stringify(user, null, 2)}</p>
</div>
);
}📈 性能基准
| 功能 | 冷启动 | 热启动 | 内存占用 | |------|--------|--------|----------| | 基础路由保护 | ~10ms | ~2ms | ~50KB | | 国际化加载 | ~20ms | ~5ms | ~100KB | | 完整功能 | ~50ms | ~10ms | ~200KB |
🤝 与其他库集成
与状态管理库集成
// 与 Redux Toolkit 集成
import { createSlice } from '@reduxjs/toolkit';
import { useUser } from '@gulibs/react-autoroutes-client';
const authSlice = createSlice({
name: 'auth',
initialState: { user: null, token: null },
reducers: {
setAuth: (state, action) => {
state.user = action.payload.user;
state.token = action.payload.token;
}
}
});
function useAuthSync() {
const dispatch = useDispatch();
const { user, token } = useUser();
useEffect(() => {
if (user && token) {
dispatch(authSlice.actions.setAuth({ user, token }));
}
}, [user, token, dispatch]);
}