UNPKG

@gulibs/vgrove-client

Version:

Client-side utilities for React auto routes

2,289 lines (1,906 loc) 101 kB
# @gulibs/vgrove-client 一个功能强大的 React 路由自动化客户端库,提供完整的路由保护、状态管理、国际化和性能优化解决方案。 ## ✨ 特性 - 🛡️ **路由保护** - 支持认证、权限、角色和自定义守卫,提供组件包装器、高阶组件和 Hook 三种使用方式 - 🏗️ **中间件系统** - 可插拔的中间件架构,支持优先级排序和开发环境限制 - 🌍 **国际化支持** - 与 @gulibs/vgrove-i18n 完美集成,支持虚拟模块、资源加载和路由集成 - 📊 **状态管理** - 基于 React Storage 的用户认证状态管理,支持 Token 自动刷新 - 💾 **存储管理** - 统一的 localStorage/sessionStorage 管理接口,支持跨标签页同步 - ⚡ **性能优化** - 内置 LRU 缓存、批处理器、性能追踪器和内存优化器,智能保护需求检测 - 🚀 **布局抖动修复** - 智能检测空配置文件,避免不必要的状态转换,提供快速通道渲染 - 🎯 **TypeScript** - 完整的类型定义支持,提供类型安全的国际化键 - 📱 **现代化** - 支持 React 18+ 和现代浏览器,兼容最新的 React Router v7 - 🔄 **热更新** - 支持开发时的热模块替换和资源热重载 - 📦 **模块化** - 按需导入,减小包大小,支持 Tree Shaking - 🛠️ **工具函数** - 丰富的实用工具函数库,涵盖路径处理、查询参数、防抖节流等 - 🔒 **安全设计** - 调试功能默认全部关闭,需要显式启用,确保生产环境安全 - 🔍 **调试支持** - 细粒度调试控制系统,支持模块化开关和浏览器开发者工具集成 ## 📦 安装 ```bash npm install @gulibs/vgrove-client # 或 pnpm add @gulibs/vgrove-client # 或 yarn add @gulibs/vgrove-client ``` ### 对等依赖 ```bash npm install react react-dom react-router lodash ``` ## 🚀 快速开始 ### 0. 配置管理(推荐) ⚠️ ⚠️ **重要:安全设计** - **调试功能默认全部关闭**,包括开发环境 - 必须显式启用调试才能看到相关日志 - 这避免了意外暴露敏感信息到调试日志中 首先初始化 VGrove Client,配置调试选项和开发者工具: ```tsx import { initVGroveClient } from '@gulibs/vgrove-client'; // 🔒 生产环境推荐配置(默认安全) initVGroveClient(); // 所有调试功能默认关闭 // 🛠️ 开发环境配置(需要显式启用调试) initVGroveClient({ debug: { enabled: true, // 必须显式启用调试系统 auth: true, // 启用认证调试 i18n: false, // 保持国际化调试关闭 performance: true, // 启用性能监控 routing: true, // 启用路由调试 storage: false // 保持存储调试关闭 }, devtools: process.env.NODE_ENV === 'development' // 只在开发环境启用 }); ``` 使用浏览器开发者工具(开发环境): ```javascript // 在浏览器控制台中使用内置开发者工具 __VGROVE_DEVTOOLS__.debug.status(); // 查看调试状态(表格形式) __VGROVE_DEVTOOLS__.debug.enable(); // 启用所有调试 __VGROVE_DEVTOOLS__.debug.enableModule('auth'); // 启用特定模块 __VGROVE_DEVTOOLS__.debug.disableModule('routing'); // 禁用特定模块 __VGROVE_DEVTOOLS__.client.getState(); // 查看客户端状态 ``` ### 1. 路由保护 ```tsx import { RouteProtectionWrapper, defineAuth, defineGuard } from '@gulibs/vgrove-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. 用户状态管理 ```tsx import { useUser } from '@gulibs/vgrove-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. 性能优化与布局抖动修复 🚀 VGrove Client 提供了先进的性能优化功能,特别针对布局抖动问题进行了专项修复。 #### 3.1 智能保护需求检测 当 `_configs.tsx` 文件为空或无实际保护需求时,系统会自动启用快速通道,避免不必要的状态转换: ```tsx // ❌ 之前:即使空配置也会执行完整状态机 // _configs.tsx (空配置) export default defineConfigs({}); // ✅ 现在:自动检测无保护需求,启用快速通道 import { RouteProtectionWrapper } from '@gulibs/vgrove-client'; function OptimizedPage() { return ( <RouteProtectionWrapper component={<YourPageComponent />} // 空配置时自动跳过状态转换,直接渲染组件 /> ); } ``` #### 3.2 布局抖动解决方案 **问题描述**:空配置文件导致页面经历 `checking` → `passed` 状态转换,造成布局抖动。 **解决方案**:智能检测 + 快速通道 ```tsx import { RouteProtectionWrapper, defineAuth, defineGuard } from '@gulibs/vgrove-client'; // ✅ 有实际保护需求 - 正常执行保护逻辑 const protectedPageConfig = { guards: [ defineAuth({ redirectTo: '/login' }), defineGuard({ condition: (ctx) => ctx.user?.permissions.includes('read'), redirectTo: '/forbidden' }) ] }; // ✅ 无保护需求 - 启用快速通道 const publicPageConfig = { guards: [], // 无守卫 middlewares: [], // 无中间件 stateRenderers: {}, // 无状态渲染器 redirectConfig: {} // 无重定向配置 }; function SmartProtectedPage() { return ( <RouteProtectionWrapper {...protectedPageConfig} component={<YourPageComponent />} // 智能检测:有保护需求时执行完整逻辑 // 无保护需求时直接渲染,0ms 状态转换 /> ); } ``` #### 3.3 性能指标对比 | 场景 | 修复前 | 修复后 | 改善 | |------|--------|--------|------| | 空配置页面首次渲染 | ~50ms + 布局抖动 | ~5ms 无抖动 | 90% ⬇️ | | 有保护需求页面 | ~50ms | ~45ms | 10% ⬇️ | | 调试输出(生产环境) | 20+ console 调用 | 0 console 调用 | 100% ⬇️ | | 内存占用 | ~200KB | ~150KB | 25% ⬇️ | #### 3.4 调试性能监控 ```tsx import { debug } from '@gulibs/vgrove-client'; // ✅ 高性能调试(仅在需要时执行) function PerformanceOptimizedComponent() { useEffect(() => { // 只有在调试启用时才执行相关逻辑 debug.performance('组件渲染完成', { timestamp: Date.now(), componentName: 'PerformanceOptimizedComponent' }); }, []); return <div>组件内容</div>; } ``` #### 3.5 验证修复效果 ```tsx // 验证工具:检查页面是否使用了快速通道 function usePerformanceVerification() { const [hasLayoutShift, setHasLayoutShift] = useState(false); const [renderTime, setRenderTime] = useState(0); useEffect(() => { const startTime = performance.now(); // 检测布局抖动 const observer = new PerformanceObserver((list) => { for (const entry of list.getEntries()) { if (entry.entryType === 'layout-shift' && entry.value > 0) { setHasLayoutShift(true); } } }); observer.observe({ entryTypes: ['layout-shift'] }); // 测量渲染时间 requestAnimationFrame(() => { setRenderTime(performance.now() - startTime); }); return () => observer.disconnect(); }, []); return { hasLayoutShift, renderTime }; } // 使用验证工具 function VerifiedPage() { const { hasLayoutShift, renderTime } = usePerformanceVerification(); return ( <div> <RouteProtectionWrapper component={<YourContent />} /> {process.env.NODE_ENV === 'development' && ( <div style={{ position: 'fixed', top: 0, right: 0, background: '#f0f0f0', padding: '8px' }}> <p>布局抖动: {hasLayoutShift ? '❌ 检测到' : '✅ 无'}</p> <p>渲染时间: {renderTime.toFixed(2)}ms</p> </div> )} </div> ); } ``` ### 4. 国际化支持 ```tsx import { I18nProvider, useI18n, createI18nClient } from '@gulibs/vgrove-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> ); } ``` ### 5. 存储管理 ```tsx import { useStorage } from '@gulibs/vgrove-client'; function StorageExample() { // 基础存储使用 const [userPrefs, setUserPrefs, removeUserPrefs] = useStorage('user_preferences', { theme: 'light', language: 'zh' }); // 使用 sessionStorage const [tempData, setTempData] = useStorage('temp_data', null, { storage: 'session' }); // 带过期时间的存储(1小时后过期) const [cacheData, setCacheData, removeCacheData, setCacheWithExpiry, isCacheExpired, cleanupCache] = useStorage( 'api_cache', {}, { storage: 'local', maxAge: 60 * 60 * 1000, // 1小时 autoCleanup: true, // 自动清理过期数据 cleanupInterval: 5 * 60 * 1000 // 每5分钟清理一次 } ); // 临时令牌存储(30分钟后过期) const [tempToken, setTempToken, removeTempToken, setTokenWithExpiry, isTokenExpired] = useStorage( 'temp_token', null, { storage: 'session', maxAge: 30 * 60 * 1000, // 30分钟 autoCleanup: true } ); return ( <div> <p>当前主题: {userPrefs.theme}</p> <button onClick={() => setUserPrefs({ ...userPrefs, theme: 'dark' })}> 切换到暗色主题 </button> <button onClick={removeUserPrefs}> 重置偏好设置 </button> {/* 缓存数据管理 */} <div> <p>缓存状态: {isCacheExpired() ? '已过期' : '有效'}</p> <button onClick={() => setCacheData({ apiData: 'some data' })}> 设置缓存(使用默认过期时间) </button> <button onClick={() => setCacheWithExpiry({ quickData: 'quick' }, 5 * 60 * 1000)}> 设置快速缓存(5分钟过期) </button> <button onClick={cleanupCache}> 手动清理过期数据 </button> </div> {/* 临时令牌管理 */} <div> <p>令牌状态: {isTokenExpired() ? '已过期' : '有效'}</p> <button onClick={() => setTempToken('temp-token-123')}> 设置临时令牌 </button> <button onClick={() => setTokenWithExpiry('extended-token', 60 * 60 * 1000)}> 设置扩展令牌(1小时) </button> </div> </div> ); } ``` ### 6. 认证状态管理 Hooks ```tsx import { useUser, useUserState, useAuthToken } from '@gulibs/vgrove-client'; // 只读用户状态 function ReadOnlyUserInfo() { const { user, isAuthenticated, token } = useUserState('auth', false); if (!isAuthenticated) return <div>未登录</div>; return ( <div> <p>用户: {user?.name}</p> <p>Token: {token?.substring(0, 20)}...</p> </div> ); } // Token 管理 function TokenManager() { const { token, refreshToken, setToken, setRefreshToken, clearAllTokens, hasToken, hasRefreshToken } = useAuthToken('auth'); return ( <div> <p>有 Token: {hasToken ? '是' : '否'}</p> <p>有 Refresh Token: {hasRefreshToken ? '是' : '否'}</p> <button onClick={() => setToken('new-token-123')}> 设置新 Token </button> <button onClick={clearAllTokens}> 清除所有 Token </button> </div> ); } ``` ### 7. 路由保护的不同形式 ```tsx import { RouteProtectionWrapper, withRouteProtection, useRouteProtection } from '@gulibs/vgrove-client'; // 1. 组件包装器形式(已展示) function ComponentWrapper() { return ( <RouteProtectionWrapper guards={[authGuard]} component={<ProtectedContent />} /> ); } // 2. 高阶组件形式 const ProtectedComponent = withRouteProtection(MyComponent, { guards: [authGuard, adminGuard], loadingElement: <div>验证中...</div> }); function App() { return <ProtectedComponent prop1="value1" />; } // 3. Hook 形式 function HookBasedProtection() { const { isLoading, error, hasAccess, user } = useRouteProtection([authGuard]); if (isLoading) return <div>检查访问权限...</div>; if (error) return <div>错误: {error}</div>; if (!hasAccess) return <div>访问被拒绝</div>; return ( <div> <h1>受保护的内容</h1> <p>欢迎, {user?.name}</p> </div> ); } ``` ## 🛡️ 认证与守卫系统完全指南 ### 认证 vs 守卫的区别 | 特性 | Auth (认证) | Guard (守卫) | |------|-------------|--------------| | **主要用途** | 身份验证 | 权限控制 | | **检查内容** | 用户是否已登录 | 用户是否有权限访问 | | **失败后果** | 重定向到登录页 | 重定向到错误页 / 拒绝页 | | **适用场景** | 保护需要登录的页面 | 保护需要特定权限的页面 | | **执行顺序** | 优先执行 | 在认证通过后执行 | ### 🔐 认证系统详解 #### 1. 基础认证 - defineAuth **最简单的认证方式,适合基础的登录检查** ```tsx import { defineAuth } from '@gulibs/vgrove-client'; // 页面级认证文件:pages/dashboard/auth.ts export default defineAuth({ name: 'dashboard-auth', // 认证名称(可选) redirectTo: '/login', // 失败时重定向路径 errorMessage: '请先登录访问仪表板', // 错误消息 check: (context) => { // 认证检查函数 // context 包含:path, params, query, user, roles, permissions, data const token = localStorage.getItem('auth_token'); // ⚠️ 关键:必须返回 boolean 值 return !!token; } }); ``` **❌ 常见错误:** ```tsx // 错误1:没有返回值 check: (context) => { const token = localStorage.getItem('auth_token'); // ❌ 缺少 return 语句 } // 错误2:返回非布尔值 check: (context) => { return localStorage.getItem('auth_token'); // ❌ 返回 string | null } // 错误3:异步函数但没有正确处理 check: (context) => { return fetch('/api/verify').then(res => res.ok); // ❌ 返回 Promise } ``` **✅ 正确用法:** ```tsx // 同步检查 check: (context) => { const token = localStorage.getItem('auth_token'); return !!token; // ✅ 返回 boolean } // 异步检查 check: async (context) => { try { const response = await fetch('/api/verify', { headers: { Authorization: `Bearer ${context.user?.token}` } }); return response.ok; // ✅ 返回 boolean } catch (error) { console.error('Auth check failed:', error); return false; // ✅ 异常时返回 false } } ``` #### 2. 智能认证 - defineSmartAuth ⭐ **推荐使用!自动处理循环重定向,防止常见错误** ```tsx import { defineSmartAuth } from '@gulibs/vgrove-client'; // 页面级认证文件:pages/auth.ts (根目录) export default defineSmartAuth({ name: 'root-smart-auth', redirectTo: '/login', errorMessage: '请先登录后访问应用', // 🚀 智能特性配置 enableLoopDetection: true, // 启用循环重定向检测 excludeCommonPublicPaths: true, // 自动排除常见公共路径 publicPaths: [ // 自定义公共路径 '/about', '/help', '/docs/*', // 支持通配符 '/api/*' ], check: (context) => { // 智能认证会自动处理: // 1. 如果是公共路径,直接允许访问 // 2. 如果检测到循环重定向,自动阻止 // 3. 然后才执行这里的检查逻辑 const token = localStorage.getItem('auth_token'); console.log('🔐 Smart Auth Check:', { path: context.path, hasToken: !!token, user: context.user }); return !!token; } }); ``` **内置公共路径列表:** ```tsx // defineSmartAuth 自动排除这些路径: const DEFAULT_PUBLIC_PATHS = [ '/login', '/signin', '/sign-in', '/register', '/signup', '/sign-up', '/auth/login', '/auth/signin', '/auth/register', '/auth/signup', '/reset-password', '/forgot-password', '/verify-email', '/verify', '/public', '/' ]; ``` #### 3. 根目录认证 - defineRootAuth **专用于整个应用的登录保护** ```tsx import { defineRootAuth } from '@gulibs/vgrove-client'; // 页面级认证文件:pages/auth.ts export default defineRootAuth({ redirectTo: '/login', errorMessage: '请先登录后访问应用', publicPaths: ['/help', '/about'], // 自定义公共路径 check: (context) => { const token = localStorage.getItem('auth_token'); return !!token; } }); ``` ### 🛡️ 守卫系统详解 #### 1. 自定义守卫 - defineGuard **适用于复杂的业务逻辑判断** ```tsx import { defineGuard } from '@gulibs/vgrove-client'; // 页面级守卫文件:pages/admin/guard.ts export default defineGuard({ name: 'admin-access', // 守卫名称 type: 'custom', // 守卫类型 redirectTo: '/forbidden', // 失败时重定向 errorMessage: '需要管理员权限访问此页面', // 错误消息 condition: (context) => { // 守卫条件函数 // context 包含:path, params, query, user, permissions, roles, data // 检查用户角色 if (!context.user?.role) return false; // 检查是否是管理员 const isAdmin = context.user.role === 'admin'; // 检查时间限制(示例:只允许工作时间访问) const now = new Date(); const workingHours = now.getHours() >= 9 && now.getHours() <= 18; return isAdmin && workingHours; } }); ``` #### 2. 角色守卫 - defineRoleGuard **基于用户角色的访问控制** ```tsx import { defineRoleGuard } from '@gulibs/vgrove-client'; // 在 _configs.tsx 中使用 export default defineConfigs({ guard: defineRoleGuard( ['admin', 'manager'], // 允许的角色列表 '/forbidden', // 可选:重定向路径 { // 可选:额外选项 publicPaths: ['/help'], enableLoopDetection: true } ) }); ``` #### 3. 权限守卫 - definePermissionGuard **基于具体权限的访问控制** ```tsx import { definePermissionGuard } from '@gulibs/vgrove-client'; // 在 _configs.tsx 中使用 export default defineConfigs({ guard: definePermissionGuard( ['user.write', 'user.update'], // 所需权限列表 '/no-permission', // 可选:重定向路径 { // 可选:额外选项 enableLoopDetection: true } ) }); ``` #### 4. 守卫工厂函数 **快速创建常用的守卫** ```tsx import { defineCustomGuard } from '@gulibs/vgrove-client'; // VIP 用户守卫 export const vipGuard = defineCustomGuard( 'vip-only', (context) => context.user?.membership === 'VIP', '/upgrade' ); // 时间限制守卫 export const workingHoursGuard = defineCustomGuard( 'working-hours', (context) => { const hour = new Date().getHours(); return hour >= 9 && hour <= 18; }, '/not-available' ); // 地理位置守卫 export const regionGuard = defineCustomGuard( 'region-check', async (context) => { const region = await getUserRegion(); return ['US', 'CA', 'EU'].includes(region); }, '/region-blocked' ); ``` ### 📁 使用方式对比 #### 方式 1:单文件方式 ``` src/pages/ ├── dashboard/ │ ├── auth.ts # 认证配置 │ ├── guard.ts # 守卫配置 │ └── page.tsx # 页面组件 └── admin/ ├── auth.ts ├── guard.ts └── page.tsx ``` **优点:** 功能分离清晰,易于理解 **缺点:** 文件较多,配置分散 #### 方式 2:配置文件方式 (推荐) ``` src/pages/ ├── dashboard/ │ ├── _configs.tsx # 集中配置 │ └── page.tsx # 页面组件 └── admin/ ├── _configs.tsx └── page.tsx ``` **优点:** 配置集中,支持自定义组件,功能完整 **缺点:** 单文件可能较大 **完整示例:** ```tsx // pages/dashboard/_configs.tsx import { defineConfigs, defineSmartAuth, defineGuard } from '@gulibs/vgrove-client'; import React from 'react'; // 自定义错误组件 const AccessDeniedError = ({ error, retry }: { error?: Error; retry?: () => void }) => ( <div className="access-denied"> <h2>访问被拒绝</h2> <p>{error?.message}</p> <button onClick={retry}>重试</button> <button onClick={() => window.location.href = '/login'}> 重新登录 </button> </div> ); // 自定义加载组件 const AuthLoading = ({ progress }: { progress?: number }) => ( <div className="auth-loading"> <div className="spinner" /> <p>验证身份中...</p> {progress && <div>进度: {Math.round(progress * 100)}%</div>} </div> ); export default defineConfigs({ // 智能认证配置 auth: defineSmartAuth({ name: 'dashboard-smart-auth', redirectTo: '/login', errorMessage: '请先登录访问仪表板', enableLoopDetection: true, excludeCommonPublicPaths: true, check: (context) => { const token = localStorage.getItem('auth_token'); console.log('Dashboard auth check:', { path: context.path, hasToken: !!token }); return !!token; } }), // 守卫配置 guard: defineGuard({ name: 'dashboard-access', type: 'custom', redirectTo: '/upgrade', errorMessage: '需要升级账户以访问仪表板', condition: (context) => { // 检查用户类型 if (!context.user) return false; // 免费用户不能访问仪表板 return context.user.plan !== 'free'; } }), // 自定义错误组件 error: AccessDeniedError, // 自定义加载组件 loading: AuthLoading }); ``` ### 🚨 常见错误和解决方案 #### 错误 1:循环重定向死循环 **症状:** 页面不断重定向,浏览器显示"重定向次数过多" **原因:** 登录页面设置了认证保护 ```tsx // ❌ 错误示例 // pages/login/auth.ts export default defineAuth({ redirectTo: '/login', // 重定向到自己 check: () => false // 总是返回false }); ``` **解决方案:** ```tsx // ✅ 方案1:删除登录页面的认证文件 // 直接删除 pages/login/auth.ts // ✅ 方案2:使用智能认证自动处理 export default defineSmartAuth({ redirectTo: '/login', enableLoopDetection: true, // 自动检测并阻止循环 excludeCommonPublicPaths: true, // 自动排除登录页面 check: (context) => !!context.user }); ``` #### 错误 2:守卫执行顺序混乱 **症状:** 权限检查在认证检查之前执行 ```tsx // ❌ 错误:在需要认证的页面只设置守卫 export default defineConfigs({ // 缺少 auth 配置 guard: defineGuard({ condition: (context) => context.user?.role === 'admin' // 用户可能为 null }) }); // ✅ 正确:先认证,再守卫 export default defineConfigs({ // 先进行身份认证 auth: defineSmartAuth({ redirectTo: '/login', check: (context) => !!context.user }), // 认证通过后检查权限 guard: defineGuard({ condition: (context) => { // 此时 context.user 已经确保存在 return context.user?.role === 'admin'; }, redirectTo: '/forbidden' }) }); ``` ### 🎯 最佳实践 #### 1. 分层认证架构 ``` src/pages/ ├── auth.ts # 根目录:基础身份认证 ├── public/ # 公开页面(无需认证) │ ├── login/ │ ├── register/ │ └── about/ ├── user/ # 用户区域 │ ├── _configs.tsx # 用户认证 + 基础权限 │ ├── dashboard/ │ └── profile/ ├── admin/ # 管理员区域 │ ├── _configs.tsx # 管理员认证 + 管理权限 │ ├── users/ │ └── settings/ └── premium/ # 高级功能区域 ├── _configs.tsx # 会员认证 + 会员权限 └── analytics/ ``` #### 2. 认证状态管理 ```tsx // utils/auth.ts - 集中的认证工具 export class AuthManager { private static instance: AuthManager; static getInstance(): AuthManager { if (!AuthManager.instance) { AuthManager.instance = new AuthManager(); } return AuthManager.instance; } // 获取当前用户 getCurrentUser(): User | null { try { const userData = localStorage.getItem('user_data'); return userData ? JSON.parse(userData) : null; } catch { return null; } } // 检查认证状态 isAuthenticated(): boolean { const token = localStorage.getItem('auth_token'); const user = this.getCurrentUser(); return !!(token && user && user.status === 'active'); } // 检查角色 hasRole(roles: string[]): boolean { const user = this.getCurrentUser(); return user?.roles?.some(role => roles.includes(role)) || false; } // 检查权限 hasPermission(permissions: string[]): boolean { const user = this.getCurrentUser(); return permissions.every(permission => user?.permissions?.includes(permission) ); } // 登出 logout(): void { localStorage.removeItem('auth_token'); localStorage.removeItem('user_data'); localStorage.removeItem('refresh_token'); // 触发自定义事件通知其他组件 window.dispatchEvent(new CustomEvent('authStateChange')); // 重定向到登录页 window.location.href = '/public/login'; } } // 在认证配置中使用 export default defineSmartAuth({ check: (context) => { const authManager = AuthManager.getInstance(); return authManager.isAuthenticated(); } }); ``` #### 3. 调试和开发工具 ```tsx // utils/authDebug.ts - 认证调试工具 export class AuthDebugger { private static enabled = process.env.NODE_ENV === 'development'; static log(message: string, data?: any): void { if (!this.enabled) return; console.group(`🔐 Auth Debug: ${message}`); if (data) { console.log('Data:', data); } console.log('Timestamp:', new Date().toISOString()); console.groupEnd(); } static logAuthCheck( checkName: string, result: boolean, context: any, reason?: string ): void { if (!this.enabled) return; const emoji = result ? '✅' : '❌'; console.group(`${emoji} Auth Check: ${checkName}`); console.log('Result:', result); console.log('Context:', { path: context.path, user: context.user ? 'Present' : 'Missing', roles: context.roles, permissions: context.permissions }); if (reason) console.log('Reason:', reason); console.groupEnd(); } } // 在认证配置中使用 export default defineSmartAuth({ name: 'debug-auth', redirectTo: '/login', check: (context) => { const token = localStorage.getItem('auth_token'); const result = !!token; AuthDebugger.logAuthCheck( 'Token Check', result, context, result ? 'Token found' : 'Token missing' ); return result; } }); ``` ### 🔧 调试指南 #### 浏览器控制台调试命令 ```javascript // 1. 检查当前认证状态 console.log('Auth Token:', localStorage.getItem('auth_token')); console.log('User Data:', JSON.parse(localStorage.getItem('user_data') || 'null')); // 2. 模拟登录状态 localStorage.setItem('auth_token', 'debug-token-123'); localStorage.setItem('user_data', JSON.stringify({ id: 'debug-user', name: 'Debug User', role: 'admin', status: 'active', permissions: ['read', 'write', 'admin'] })); // 3. 清除认证状态 localStorage.removeItem('auth_token'); localStorage.removeItem('user_data'); localStorage.removeItem('refresh_token'); // 4. 检查页面保护状态 console.log('Current Path:', window.location.pathname); console.log('Should be protected:', !window.location.pathname.includes('/public/')); ``` ### 📝 快速参考 #### 认证类型对比表 | 场景 | 推荐方案 | 配置示例 | |------|----------|----------| | 简单登录检查 | `defineAuth` | `check: (ctx) => !!ctx.user` | | 复杂认证逻辑 | `defineSmartAuth` | `enableLoopDetection: true` | | 根目录保护 | `defineRootAuth` | `publicPaths: ['/public/*']` | | 角色权限 | `defineRoleGuard` | `['admin', 'manager']` | | 具体权限 | `definePermissionGuard` | `['user.read', 'user.write']` | | 自定义业务逻辑 | `defineGuard` | `condition: (ctx) => {...}` | #### 记住这些关键点 - ✅ 认证函数必须返回 `boolean` - ✅ 登录页面不要设置认证保护 - ✅ 使用 `defineSmartAuth` 避免循环重定向 - ✅ 先设置 `auth` 再设置 `guard` - ✅ 异步认证要用 `async/await` - ✅ 错误处理要提供用户友好的提示 --- > **💡 提示:** 对于初学者,建议先使用 `defineSmartAuth` 和 `defineRoleGuard`,它们内置了大部分常见错误的处理逻辑。 ### 7. 高级国际化 Hooks ```tsx import { useI18nKeyValidator, useI18nNamespace, usePlural, useTranslateLocalized } from '@gulibs/vgrove-client'; // 键验证 Hook function I18nKeyValidator() { const { hasKey, validateKey, getAvailableKeys } = useI18nKeyValidator(); const checkKey = (key: string) => { if (hasKey(key)) { console.log('键存在:', key); } else { const { suggestions } = validateKey(key); console.log('键不存在,建议:', suggestions); } }; return ( <div> <button onClick={() => checkKey('welcome.title')}> 检查键: welcome.title </button> <p>可用键数量: {getAvailableKeys().length}</p> </div> ); } // 命名空间 Hook function NamespaceExample() { const { t, resources, exists } = useI18nNamespace('user'); if (!exists) return <div>命名空间不存在</div>; return ( <div> <h1>{t('profile.title')}</h1> {/* 相当于 user.profile.title */} <p>{t('profile.description')}</p> </div> ); } // 复数形式 Hook function PluralExample() { const plural = usePlural(); const [count, setCount] = useState(1); return ( <div> <p>{plural('message.count', count, { count })}</p> <button onClick={() => setCount(count + 1)}>增加</button> </div> ); } // 本地化对象翻译 Hook function LocalizedTranslation() { const translateLocalized = useTranslateLocalized(); const localizedTitle = { localizedId: 'page.title' }; return ( <div> <h1>{translateLocalized(localizedTitle)}</h1> </div> ); } ``` ### 8. I18nMessage 组件 ```tsx import { I18nMessage, I18nTitle, I18nText, I18nSwitch } from '@gulibs/vgrove-client'; // 基础用法 function MyComponent() { return ( <div> <I18nMessage id="welcome.title" defaultValue="欢迎" /> <I18nMessage id="welcome.description" params={{ name: '用户' }} defaultValue="你好,{name}!" /> </div> ); } // 使用便捷组件 function PageContent() { return ( <div> <I18nTitle id="page.title" level={1} /> <I18nText id="page.subtitle" className="text-gray-600" /> {/* 支持不同格式 */} <I18nMessage id="rich.content" format="markdown" defaultValue="**粗体** 和 *斜体* 文本" /> {/* 组件标记格式 */} <I18nMessage id="component.text" format="component" defaultValue="点击 {strong}这里{/strong} 查看更多" /> </div> ); } // 语言切换器 function LanguageSwitcher() { return ( <I18nSwitch variant="select" locales={[ { code: 'zh', label: '中文' }, { code: 'en', label: 'English' } ]} onLocaleChange={(locale) => console.log('语言切换到:', locale)} /> ); } ``` ### 9. 性能优化工具详细示例 ```tsx import { PerformanceCache, BatchProcessor, PerformanceTracker, MemoryOptimizer, Debouncer, measureAsync } from '@gulibs/vgrove-client'; // 缓存管理 function CacheExample() { const cache = useMemo(() => new PerformanceCache<string, any>(100), []); const fetchWithCache = useCallback(async (key: string) => { if (cache.has(key)) { return cache.get(key); } const data = await fetch(`/api/data/${key}`).then(res => res.json()); cache.set(key, data); return data; }, [cache]); return ( <div> <button onClick={() => fetchWithCache('user-1')}> 获取用户数据(带缓存) </button> <p>缓存大小: {cache.size()}</p> </div> ); } // 批处理示例 function BatchProcessingExample() { const batchProcessor = useMemo(() => new BatchProcessor(5, 100), []); const processBatchData = useCallback(async () => { const userIds = ['1', '2', '3', '4', '5', '6', '7', '8']; const users = await batchProcessor.processBatch( userIds, async (id) => { const response = await fetch(`/api/users/${id}`); return response.json(); } ); console.log('批处理完成:', users); }, [batchProcessor]); return ( <button onClick={processBatchData}> 批量处理用户数据 </button> ); } // 性能追踪示例 function PerformanceExample() { const tracker = useMemo(() => new PerformanceTracker(), []); const expensiveOperation = useCallback(async () => { tracker.startTimer('expensive-operation'); // 模拟耗时操作 await new Promise(resolve => setTimeout(resolve, 1000)); const duration = tracker.endTimer('expensive-operation'); console.log(`操作耗时: ${duration}ms`); tracker.increment('operation-count'); console.log(`操作次数: ${tracker.getCounter('operation-count')}`); }, [tracker]); return ( <div> <button onClick={expensiveOperation}> 执行耗时操作 </button> <button onClick={() => console.log(tracker.getStats())}> 查看性能统计 </button> </div> ); } // 防抖示例 function DebounceExample() { const debouncer = useMemo(() => new Debouncer(), []); const [searchTerm, setSearchTerm] = useState(''); const handleSearch = useCallback((term: string) => { debouncer.debounce('search', () => { console.log('搜索:', term); // 执行实际搜索 }, 500); }, [debouncer]); return ( <input value={searchTerm} onChange={(e) => { setSearchTerm(e.target.value); handleSearch(e.target.value); }} placeholder="搜索(防抖 500ms)" /> ); } // 内存优化示例 function MemoryOptimizationExample() { useEffect(() => { const memoryOptimizer = MemoryOptimizer.getInstance(); // 添加清理任务 memoryOptimizer.addCleanupTask(() => { console.log('清理缓存数据'); // 清理各种缓存 }); // 启动定期清理(5分钟) memoryOptimizer.startPeriodicCleanup(5 * 60 * 1000); return () => { // 组件卸载时进行清理 memoryOptimizer.runCleanup(); }; }, []); return <div>内存优化已启用</div>; } // 异步性能测量 function AsyncMeasurementExample() { const measureApiCall = useCallback(async () => { const result = await measureAsync('api-call', async () => { const response = await fetch('/api/data'); return response.json(); }); console.log('API 调用结果:', result); console.log('耗时:', result.duration, 'ms'); }, []); return ( <button onClick={measureApiCall}> 测量 API 调用性能 </button> ); } ``` ### 10. 工具函数详细示例 ```tsx import { normalizePath, parseQuery, buildQuery, updateQuery, debounce, throttle, deepMerge, deepClone, unique, groupBy, isEmpty, safeParseInt, toCamelCase, toKebabCase, capitalize, truncate, safely, safelyAsync } from '@gulibs/vgrove-client'; function UtilityFunctionsExample() { // 路径处理 const normalizedPath = normalizePath('//api//users//profile//'); console.log(normalizedPath); // '/api/users/profile' // 查询参数处理 const queryParams = parseQuery('?name=john&age=25&active=true'); console.log(queryParams); // { name: 'john', age: '25', active: 'true' } const queryString = buildQuery({ name: 'jane', age: 30, active: false }); console.log(queryString); // 'name=jane&age=30&active=false' const updatedUrl = updateQuery('/users', { page: 2, size: 10 }); console.log(updatedUrl); // '/users?page=2&size=10' // 防抖和节流 const debouncedSearch = debounce((term: string) => { console.log('搜索:', term); }, 500); const throttledScroll = throttle(() => { console.log('滚动事件'); }, 100); // 对象操作 const original = { a: 1, b: { c: 2 } }; const cloned = deepClone(original); const merged = deepMerge({ x: 1 }, { y: 2, z: { a: 3 } }); // 数组操作 const items = [1, 2, 2, 3, 4, 4, 5]; const uniqueItems = unique(items); console.log(uniqueItems); // [1, 2, 3, 4, 5] const users = [ { name: 'Alice', role: 'admin' }, { name: 'Bob', role: 'user' }, { name: 'Charlie', role: 'admin' } ]; const groupedUsers = groupBy(users, user => user.role); console.log(groupedUsers); // { admin: [Alice, Charlie], user: [Bob] } // 字符串操作 const camelCase = toCamelCase('hello-world-example'); console.log(camelCase); // 'helloWorldExample' const kebabCase = toKebabCase('HelloWorldExample'); console.log(kebabCase); // 'hello-world-example' const capitalized = capitalize('hello world'); console.log(capitalized); // 'Hello world' const truncated = truncate('This is a very long text', 10); console.log(truncated); // 'This is a...' // 安全操作 const safeValue = safely(() => JSON.parse('invalid json'), null); console.log(safeValue); // null const safeAsyncValue = safelyAsync( async () => { const response = await fetch('/api/data'); return response.json(); }, { error: 'Failed to load' } ); // 类型检查和转换 const emptyCheck = isEmpty(''); // true const numberValue = safeParseInt('123abc', 0); // 123 return ( <div> <h3>工具函数示例</h3> <p>标准化路径: {normalizedPath}</p> <p>解析查询: {JSON.stringify(queryParams)}</p> <p>构建查询: {queryString}</p> <p>唯一数组: {JSON.stringify(uniqueItems)}</p> <input onChange={(e) => debouncedSearch(e.target.value)} placeholder="防抖搜索" /> <div onScroll={throttledScroll} style={{ height: '100px', overflow: 'scroll' }} > 滚动内容... </div> </div> ); } ``` ### 11. 国际化路由集成 ```tsx import { createI18nClient, createI18nRouteLoader, createI18nHandle, useRouteI18n } from '@gulibs/vgrove-client'; // 创建国际化客户端 const i18nClient = createI18nClient({ defaultLocale: 'zh', supportedLocales: ['zh', 'en'], basePath: '/api/locales' }); // 创建路由加载器 const i18nLoader = createI18nRouteLoader(i18nClient, { preloadAll: false, delay: 0 }); // 路由配置 export const route = { path: '/products/:id', loader: i18nLoader, handle: createI18nHandle({ meta: { title: 'products.detail.title', description: 'products.detail.description' }, pageExtras: (context) => { const { params, query } = context; return ( <div> <ProductBreadcrumb productId={params.id} /> {query.variant && <VariantSelector variant={query.variant} />} </div> ); } }) }; // 组件使用 function ProductDetailPage() { const { locale, isLoaded, getLocalizedUrl, waitForI18n } = useRouteI18n(); const { t } = useI18n(); const { meta, pageExtras } = useHandle(); // 等待国际化资源加载 useEffect(() => { waitForI18n().then(() => { console.log('国际化资源加载完成'); }); }, [waitForI18n]); if (!isLoaded) { return <div>Loading translations...</div>; } return ( <div> <h1>{t('products.detail.title')}</h1> {/* 动态生成的页面额外内容 */} {pageExtras} {/* 语言切换按钮 */} <button onClick={() => window.location.href = getLocalizedUrl('en')}> English </button> </div> ); } ``` ### 12. 页面配置和 Hooks ```tsx import { useHandle, defineHandle, usePageMeta, useBreadcrumbs, useDocumentTitle } from '@gulibs/vgrove-client'; // 定义页面处理配置 export const handle = defineHandle({ meta: { title: 'page.title', description: '页面描述' }, breadcrumbs: { href: '/current-page', children: '当前页面' }, // pageExtras 可以是静态内容或函数 pageExtras: (context) => { // 可以访问路由上下文信息 const { path, params, query, matches, handle } = context; return ( <div> <p>当前路径: {path}</p> <p>路由参数: {JSON.stringify(params)}</p> <p>查询参数: {JSON.stringify(query)}</p> <p>匹配的路由数量: {matches.length}</p> {params.id && <p>商品ID: {params.id}</p>} {query.tab && <p>当前标签: {query.tab}</p>} </div> ); } }); function MyPage() { // 获取页面配置 const { meta, breadcrumbs, pageExtras } = useHandle(); return ( <div> <h1>{meta?.title}</h1> {/* 渲染动态生成的额外内容 */} {pageExtras} {/* 页面内容 */} </div> ); } // 使用页面元数据 Hook function PageWithMeta() { const meta = usePageMeta(handle, { enableI18n: true }); const updateTitle = useDocumentTitle(handle, 'My App', { enableI18n: true }); useEffect(() => { updateTitle(); // 设置文档标题 }, [updateTitle]); return ( <div> <h1>{meta?.title}</h1> <p>{meta?.description}</p> </div> ); } // 使用面包屑 Hook function PageWithBreadcrumbs() { const breadcrumbs = useBreadcrumbs(handle, { enableI18n: true, includeMatches: true }); return ( <div> <nav> {breadcrumbs.map((breadcrumb, index) => ( <span key={index}> {index > 0 && ' > '} <a href={breadcrumb.href}>{breadcrumb.children}</a> </span> ))} </nav> {/* 页面内容 */} </div> ); } ``` ### 13. 综合应用示例 ```tsx import { RouteProtectionWrapper, I18nProvider, useUser, useI18n, I18nMessage, createI18nClient, defineAuth, defineGuard, PerformanceCache, useStorage } from '@gulibs/vgrove-client'; // 创建国际化客户端 const i18nClient = createI18nClient({ defaultLocale: 'zh', supportedLocales: ['zh', 'en'], persistence: { enabled: true }, detectBrowserLanguage: true }); // 创建缓存 const appCache = new PerformanceCache<string, any>(200); // 定义认证守卫 const authGuard = defineAuth({ redirectTo: '/login', errorMessage: 'auth.required' }); // 定义VIP守卫 const vipGuard = defineGuard({ name: 'vip-only', condition: (context) => context.user?.membership === 'VIP', redirectTo: '/upgrade', errorMessage: 'vip.required' }); // 主应用组件 function App() { return ( <I18nProvider client={i18nClient}> <AppContent /> </I18nProvider> ); } function AppContent() { const { isAuthenticated, user } = useUser({ storagePrefix: 'myapp', autoRefreshInterval: 15 * 60 * 1000, loginApi: async (credentials) => { const response = await fetch('/api/auth/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(credentials) }); return response.json(); } }); const { locale, setLocale } = useI18n(); const [userPreferences, setUserPreferences] = useStorage('user_prefs', { theme: 'light', notifications: true }); return ( <div> {/* 顶部导航 */} <header> <I18nMessage id="app.title" defaultValue="我的应用" /> {isAuthenticated && ( <span> <I18nMessage id="welcome.user" params={{ name: user?.name }} defaultValue="欢迎, {name}!" /> </span> )} <button onClick={() => setLocale(locale === 'zh' ? 'en' : 'zh')}> {locale === 'zh' ? 'English' : '中文'} </button> </header> {/* 主内容区 */} <main> {/* 公开页面 */} <PublicPage /> {/* 需要认证的页面 */} <RouteProtectionWrapper guards={[authGuard]} loadingElement={ <I18nMessage id="loading.auth" defaultValue="验证中..." /> } component={<ProtectedPage />} /> {/* 需要VIP权限的页面 */} <RouteProtectionWrapper guards={[authGuard, vipGuard]} loadingElement={ <I18nMessage id="loading.vip" defaultValue="检查VIP权限..." /> } component={<VIPPage />} /> </main> {/* 用户偏好设置 */} <aside> <h3> <I18nMessage id="settings.title" defaultValue="设置" /> </h3> <label> <input type="checkbox" checked={userPreferences.notifications} onChange={(e) => setUserPreferences({ ...userPreferences, notifications: e.target.checked }) } /> <I18nMessage id="settings.notifications" defaultValue="启用通知" /> </label> </aside> </div> ); } // 公开页面 function PublicPage() { return ( <div> <I18nMessage id="public.welcome" defaultValue="欢迎访问我们的网站" /> </div> ); } // 受保护页面 function ProtectedPage() { const { user } = useUser(); return ( <div> <I18nMessage id="protected.welcome" params={{ name: user?.name }} defaultValue="欢迎进入会员区域, {name}!" /> </div> ); } // VIP页面 function VIPPage() { const { user } = useUser(); return ( <div> <I18nMessage id="vip.welcome" params={{ name: user?.name }} defaultValue="欢迎尊贵的VIP用户, {name}!" /> </div> ); } ``` ## 🎯 高级用法 ### 中间件系统 ```tsx import { defineMiddleware } from '@gulibs/vgrove-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 />} /> ``` ### 国际化资源加载器 ```tsx import { createI18nLoader, ViteI18nLoader, createI18nClient } from '@gulibs/vgrove-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?.()); ``` ### 复杂路由守卫 ```tsx // 角色守卫 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' }); ``` ### 性能优化工具 ```tsx import { PerformanceCache, BatchProcessor, PerformanceTracker, MemoryOptimizer } from '@gulibs/vgrove-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(); }; }, []); } ``` ### 工具函数和实用程序 ```tsx import { normalizePath, parseQuery, buildQuery, debounce, throttle, deepMerge, safeParseInt, isEmpty, measureTime } from '@gulibs/vgrove-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:** ```typescript interface RouteProtectionWrapperProps { guards?: (AuthOptions | GuardOptions | Function)[]; middlewares?: (MiddlewareOptions | Function)[]; loadingElement?: React.ReactNode; component: React.ReactNode; children?: React.ReactNode; } ``` #### `defineAuth(options: AuthOptions)` 定义认证守卫。 ```typescript interface AuthOptions { name?: string; redirectTo?: string; errorMessage?: string; check?: (context: AuthContext) => boolean | Promise<boolean>; roles?: string[]; permissions?: string[]; } ``` #### `defineGuard(options: GuardOptions)` 定义自定义守卫。 ```typescript interface GuardOptions { name?: string; type?: 'auth' | 'role' | 'permission' | 'custom'; redirectTo?: string; errorMessage?: string; condition: (context: GuardContext) => boolean | Promise<boolean>; } ``` #### `defineMiddleware(options: MiddlewareOptions)` 定义中间件。 ```typescript interface MiddlewareOptions { name?: string; priority?: number; devOnly?: boolean; handler: (context: MiddlewareContext, next: () => Promise<void> | void) => Promise<void> | void; } ``` ### 状态管理 #### `useUser<TUser, TCredentials>(options: UseUserOptions)` 用户状态管理 Hook。 **返回值:** ```typescript 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?)` 基于 @gulibs/react-storage 的通用存储 Hook,支持过期时间。 ```typescript interface UseStorageOptions { /** 存储类型 */ storage?: 'local' | 'session'; /** 过期时间(毫秒),0 表示永不过期 */ maxAge?: number; /** 是否自动清理过期数据 */ autoCleanup?: boolean; /** 清理间隔(毫秒),默认 5 分钟 */ cleanupInterval?: number; } function useStorage<T>( key: string, defaultValue: T, options?: UseStorageOptions ): [ T, // 当前值 (value: T) => void, // 设置值函数 () => void,