Initial frontend repository commit.
Include app source and update .gitignore to exclude local release artifacts and signing files. Made-with: Cursor
This commit is contained in:
363
src/hooks/usePrefetch.ts
Normal file
363
src/hooks/usePrefetch.ts
Normal file
@@ -0,0 +1,363 @@
|
||||
/**
|
||||
* 数据预取Hook
|
||||
* 用于关键数据的预加载,提升用户体验
|
||||
*
|
||||
* 特性:
|
||||
* - 应用启动时预取关键数据
|
||||
* - 页面导航时预取相关数据
|
||||
* - 智能预取策略(基于用户行为)
|
||||
* - 并发控制和优先级管理
|
||||
*/
|
||||
|
||||
import { useCallback, useEffect, useRef } from 'react';
|
||||
import { postManager } from '../stores/postManager';
|
||||
import { groupManager } from '../stores/groupManager';
|
||||
import { userManager } from '../stores/userManager';
|
||||
import { messageManager } from '../stores/messageManager';
|
||||
|
||||
// ==================== 预取配置 ====================
|
||||
|
||||
/** 预取任务优先级 */
|
||||
enum Priority {
|
||||
HIGH = 0,
|
||||
MEDIUM = 1,
|
||||
LOW = 2,
|
||||
}
|
||||
|
||||
/** 预取任务接口 */
|
||||
interface PrefetchTask {
|
||||
key: string;
|
||||
executor: () => Promise<any>;
|
||||
priority: Priority;
|
||||
}
|
||||
|
||||
/** 预取服务类 */
|
||||
class PrefetchService {
|
||||
/** 预取任务队列 */
|
||||
private queue: PrefetchTask[] = [];
|
||||
|
||||
/** 是否正在处理队列 */
|
||||
private isProcessing = false;
|
||||
|
||||
/** 最大并发数 */
|
||||
private readonly MAX_CONCURRENCY = 3;
|
||||
|
||||
/** 当前活跃的任务数 */
|
||||
private activeCount = 0;
|
||||
|
||||
/** 是否启用调试日志 */
|
||||
private readonly DEBUG = __DEV__ || false;
|
||||
|
||||
/**
|
||||
* 添加预取任务到队列
|
||||
* @param task 预取任务
|
||||
*/
|
||||
schedule(task: PrefetchTask): void {
|
||||
// 检查是否已有相同key的任务
|
||||
const existingIndex = this.queue.findIndex(t => t.key === task.key);
|
||||
if (existingIndex >= 0) {
|
||||
// 如果新任务优先级更高,替换旧任务
|
||||
if (task.priority < this.queue[existingIndex].priority) {
|
||||
this.queue[existingIndex] = task;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 按优先级插入队列
|
||||
let insertIndex = this.queue.findIndex(t => t.priority > task.priority);
|
||||
if (insertIndex === -1) {
|
||||
insertIndex = this.queue.length;
|
||||
}
|
||||
this.queue.splice(insertIndex, 0, task);
|
||||
|
||||
if (this.DEBUG) {
|
||||
console.log(`[Prefetch] 添加任务: ${task.key}, 优先级: ${Priority[task.priority]}`);
|
||||
}
|
||||
|
||||
this.processQueue();
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理预取队列
|
||||
*/
|
||||
private async processQueue(): Promise<void> {
|
||||
if (this.isProcessing || this.queue.length === 0) return;
|
||||
|
||||
this.isProcessing = true;
|
||||
|
||||
while (this.queue.length > 0 && this.activeCount < this.MAX_CONCURRENCY) {
|
||||
const task = this.queue.shift();
|
||||
if (!task) break;
|
||||
|
||||
this.activeCount++;
|
||||
|
||||
// 异步执行任务
|
||||
this.executeTask(task).finally(() => {
|
||||
this.activeCount--;
|
||||
this.processQueue();
|
||||
});
|
||||
}
|
||||
|
||||
this.isProcessing = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行单个预取任务
|
||||
*/
|
||||
private async executeTask(task: PrefetchTask): Promise<void> {
|
||||
try {
|
||||
if (this.DEBUG) {
|
||||
console.log(`[Prefetch] 执行任务: ${task.key}`);
|
||||
}
|
||||
await task.executor();
|
||||
if (this.DEBUG) {
|
||||
console.log(`[Prefetch] 任务完成: ${task.key}`);
|
||||
}
|
||||
} catch (error) {
|
||||
if (this.DEBUG) {
|
||||
console.warn(`[Prefetch] 任务失败: ${task.key}`, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空预取队列
|
||||
*/
|
||||
clearQueue(): void {
|
||||
this.queue = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取队列长度
|
||||
*/
|
||||
getQueueLength(): number {
|
||||
return this.queue.length;
|
||||
}
|
||||
}
|
||||
|
||||
/** 全局预取服务实例 */
|
||||
const prefetchService = new PrefetchService();
|
||||
|
||||
// ==================== 预取函数 ====================
|
||||
|
||||
/**
|
||||
* 预取帖子数据
|
||||
* @param types 帖子类型数组
|
||||
*/
|
||||
function prefetchPosts(types: string[] = ['recommend', 'hot']): void {
|
||||
types.forEach((type, index) => {
|
||||
prefetchService.schedule({
|
||||
key: `posts:${type}:1`,
|
||||
executor: () => postManager.getPosts(type, 1, 20),
|
||||
priority: index === 0 ? Priority.HIGH : Priority.MEDIUM,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 预取会话数据
|
||||
*/
|
||||
function prefetchConversations(): void {
|
||||
prefetchService.schedule({
|
||||
key: 'conversations:list',
|
||||
executor: async () => {
|
||||
await messageManager.initialize();
|
||||
await messageManager.fetchConversations(true);
|
||||
return messageManager.getConversations();
|
||||
},
|
||||
priority: Priority.HIGH,
|
||||
});
|
||||
|
||||
// 同时预取未读数
|
||||
prefetchService.schedule({
|
||||
key: 'conversations:unread',
|
||||
executor: async () => {
|
||||
await messageManager.initialize();
|
||||
await messageManager.fetchUnreadCount();
|
||||
return messageManager.getUnreadCount();
|
||||
},
|
||||
priority: Priority.HIGH,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 预取用户信息
|
||||
*/
|
||||
function prefetchUserInfo(): void {
|
||||
prefetchService.schedule({
|
||||
key: 'users:me',
|
||||
executor: () => userManager.getCurrentUser(),
|
||||
priority: Priority.HIGH,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 预取会话详情
|
||||
* @param conversationIds 会话ID数组
|
||||
*/
|
||||
function prefetchConversationDetails(conversationIds: string[]): void {
|
||||
conversationIds.forEach(id => {
|
||||
prefetchService.schedule({
|
||||
key: `conversations:detail:${id}`,
|
||||
executor: async () => {
|
||||
await messageManager.initialize();
|
||||
return messageManager.fetchConversationDetail(id);
|
||||
},
|
||||
priority: Priority.LOW,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 预取群组成员
|
||||
* @param groupIds 群组ID数组
|
||||
*/
|
||||
function prefetchGroupMembers(groupIds: string[]): void {
|
||||
groupIds.forEach(id => {
|
||||
prefetchService.schedule({
|
||||
key: `groups:members:${id}`,
|
||||
executor: async () => {
|
||||
const response = await groupManager.getMembers(id, 1, 50);
|
||||
return response.list || [];
|
||||
},
|
||||
priority: Priority.LOW,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 应用启动时预取
|
||||
* 预取最关键的数据
|
||||
*/
|
||||
function prefetchOnAppLaunch(): void {
|
||||
if (__DEV__) {
|
||||
console.log('[Prefetch] 开始应用启动预取');
|
||||
}
|
||||
|
||||
// 高优先级:用户信息
|
||||
prefetchUserInfo();
|
||||
|
||||
// 高优先级:会话列表和未读数
|
||||
prefetchConversations();
|
||||
|
||||
// 中优先级:帖子列表
|
||||
prefetchPosts(['recommend']);
|
||||
}
|
||||
|
||||
/**
|
||||
* 进入消息页面时预取
|
||||
*/
|
||||
function prefetchMessageScreen(): void {
|
||||
// 获取当前会话列表,预取前几个会话的详情
|
||||
const conversations = messageManager.getConversations();
|
||||
const topConversationIds = conversations.slice(0, 5).map((c: any) => c.id);
|
||||
|
||||
// 预取会话详情
|
||||
prefetchConversationDetails(topConversationIds);
|
||||
|
||||
// 预取群组成员(如果是群聊)
|
||||
const groupIds = conversations
|
||||
.filter((c: any) => c.type === 'group' && c.group?.id)
|
||||
.slice(0, 3)
|
||||
.map((c: any) => String(c.group!.id));
|
||||
|
||||
prefetchGroupMembers(groupIds);
|
||||
}
|
||||
|
||||
/**
|
||||
* 进入首页时预取
|
||||
*/
|
||||
function prefetchHomeScreen(): void {
|
||||
prefetchPosts(['recommend', 'hot', 'latest']);
|
||||
}
|
||||
|
||||
// ==================== React Hook ====================
|
||||
|
||||
/**
|
||||
* 数据预取Hook
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* const { prefetchOnAppLaunch, prefetchMessageScreen } = usePrefetch();
|
||||
*
|
||||
* useEffect(() => {
|
||||
* prefetchOnAppLaunch();
|
||||
* }, []);
|
||||
* ```
|
||||
*/
|
||||
export function usePrefetch() {
|
||||
/** 是否已完成初始预取 */
|
||||
const hasInitialPrefetched = useRef(false);
|
||||
|
||||
/** 预取帖子 */
|
||||
const prefetchPostsData = useCallback((types?: string[]) => {
|
||||
prefetchPosts(types);
|
||||
}, []);
|
||||
|
||||
/** 预取会话 */
|
||||
const prefetchConversationsData = useCallback(() => {
|
||||
prefetchConversations();
|
||||
}, []);
|
||||
|
||||
/** 预取用户信息 */
|
||||
const prefetchUserData = useCallback(() => {
|
||||
prefetchUserInfo();
|
||||
}, []);
|
||||
|
||||
/** 应用启动预取 */
|
||||
const doPrefetchOnAppLaunch = useCallback(() => {
|
||||
if (hasInitialPrefetched.current) return;
|
||||
hasInitialPrefetched.current = true;
|
||||
prefetchOnAppLaunch();
|
||||
}, []);
|
||||
|
||||
/** 消息页面预取 */
|
||||
const doPrefetchMessageScreen = useCallback(() => {
|
||||
prefetchMessageScreen();
|
||||
}, []);
|
||||
|
||||
/** 首页预取 */
|
||||
const doPrefetchHomeScreen = useCallback(() => {
|
||||
prefetchHomeScreen();
|
||||
}, []);
|
||||
|
||||
/** 清空预取队列 */
|
||||
const clearPrefetchQueue = useCallback(() => {
|
||||
prefetchService.clearQueue();
|
||||
}, []);
|
||||
|
||||
/** 获取队列长度 */
|
||||
const getQueueLength = useCallback(() => {
|
||||
return prefetchService.getQueueLength();
|
||||
}, []);
|
||||
|
||||
return {
|
||||
// 基础预取方法
|
||||
prefetchPosts: prefetchPostsData,
|
||||
prefetchConversations: prefetchConversationsData,
|
||||
prefetchUserInfo: prefetchUserData,
|
||||
|
||||
// 场景预取方法
|
||||
prefetchOnAppLaunch: doPrefetchOnAppLaunch,
|
||||
prefetchMessageScreen: doPrefetchMessageScreen,
|
||||
prefetchHomeScreen: doPrefetchHomeScreen,
|
||||
|
||||
// 工具方法
|
||||
clearPrefetchQueue,
|
||||
getQueueLength,
|
||||
};
|
||||
}
|
||||
|
||||
// ==================== 导出 ====================
|
||||
|
||||
export {
|
||||
prefetchPosts,
|
||||
prefetchConversations,
|
||||
prefetchUserInfo,
|
||||
prefetchOnAppLaunch,
|
||||
prefetchMessageScreen,
|
||||
prefetchHomeScreen,
|
||||
prefetchService,
|
||||
};
|
||||
|
||||
export default usePrefetch;
|
||||
Reference in New Issue
Block a user