完成请求推送,但组装mes还有问题
This commit is contained in:
382
APP_INITIALIZATION_OPTIMIZATION.md
Normal file
382
APP_INITIALIZATION_OPTIMIZATION.md
Normal file
@@ -0,0 +1,382 @@
|
||||
# 应用启动预加载优化
|
||||
|
||||
## 概述
|
||||
|
||||
为了提升用户体验,避免"先有鸡还是先有蛋"的问题,应用在启动时会自动加载所有必要的配置数据。
|
||||
|
||||
## 预加载的数据
|
||||
|
||||
### 1. API 配置 ✅
|
||||
|
||||
**为什么需要预加载:**
|
||||
- 聊天功能依赖 API 配置才能调用 LLM
|
||||
- 用户可能直接开始聊天而不访问 API 配置页面
|
||||
|
||||
**加载逻辑:**
|
||||
```javascript
|
||||
// 1. 加载配置文件列表
|
||||
await apiConfigStore.fetchProfiles();
|
||||
|
||||
// 2. 如果有配置但没有选中,自动加载第一个
|
||||
if (profiles.length > 0 && !currentProfile) {
|
||||
await fetchProfile(profiles[0].id);
|
||||
}
|
||||
```
|
||||
|
||||
**日志输出:**
|
||||
```
|
||||
[App] ✅ API 配置文件列表加载完成
|
||||
[App] 📝 自动加载第一个配置文件: Default
|
||||
[App] ✅ API 配置详情加载完成
|
||||
```
|
||||
|
||||
### 2. 预设列表 ✅
|
||||
|
||||
**为什么需要预加载:**
|
||||
- 聊天时使用预设中的参数(temperature、max_tokens 等)
|
||||
- 提示词组件来自预设配置
|
||||
|
||||
**加载逻辑:**
|
||||
```javascript
|
||||
// 1. 加载预设列表
|
||||
await presetStore.fetchPresets();
|
||||
|
||||
// 2. 如果没有选中预设,自动选择第一个
|
||||
if (!selectedPreset && presets.length > 0) {
|
||||
selectPreset(presets[0].name);
|
||||
}
|
||||
```
|
||||
|
||||
**日志输出:**
|
||||
```
|
||||
[App] ✅ 预设列表加载完成
|
||||
[App] 📝 自动选择第一个预设: Default
|
||||
```
|
||||
|
||||
### 3. 角色卡列表 ✅
|
||||
|
||||
**为什么需要预加载:**
|
||||
- 用户打开应用后需要立即看到可用的角色
|
||||
- 避免切换到角色卡标签页时才加载的延迟
|
||||
|
||||
**加载逻辑:**
|
||||
```javascript
|
||||
await characterStore.fetchCharacters();
|
||||
```
|
||||
|
||||
**日志输出:**
|
||||
```
|
||||
[App] ✅ 角色卡列表加载完成
|
||||
```
|
||||
|
||||
### 4. 世界书列表 ✅
|
||||
|
||||
**为什么需要预加载:**
|
||||
- 角色卡可能绑定世界书
|
||||
- 聊天时需要激活世界书条目
|
||||
|
||||
**加载逻辑:**
|
||||
```javascript
|
||||
await worldBookStore.fetchWorldBooks();
|
||||
```
|
||||
|
||||
**日志输出:**
|
||||
```
|
||||
[App] ✅ 世界书列表加载完成
|
||||
```
|
||||
|
||||
## 不需要预加载的数据
|
||||
|
||||
### Token 使用统计 ❌
|
||||
|
||||
**原因:**
|
||||
- 这是查看型数据,不是必需的配置
|
||||
- 数据量可能较大
|
||||
- 用户不一定需要查看
|
||||
|
||||
**加载时机:**
|
||||
- 用户切换到 Token 使用标签页时再加载
|
||||
|
||||
### 任务列表 ❌
|
||||
|
||||
**原因:**
|
||||
- 任务是动态生成的
|
||||
- 每次聊天时根据选项创建
|
||||
- 不需要持久化
|
||||
|
||||
**加载时机:**
|
||||
- 聊天时根据需要创建
|
||||
|
||||
### 正则规则 ❌
|
||||
|
||||
**原因:**
|
||||
- 使用时才加载
|
||||
- 可能按角色或预设分别加载
|
||||
|
||||
**加载时机:**
|
||||
- 处理消息时按需加载
|
||||
|
||||
## 技术实现
|
||||
|
||||
### 并行加载
|
||||
|
||||
使用 `Promise.allSettled` 并行加载所有数据,而不是串行:
|
||||
|
||||
```javascript
|
||||
Promise.allSettled([
|
||||
loadApiConfig(),
|
||||
loadPresets(),
|
||||
loadCharacters(),
|
||||
loadWorldBooks()
|
||||
]).then((results) => {
|
||||
// 处理结果
|
||||
});
|
||||
```
|
||||
|
||||
**优势:**
|
||||
- ⚡ 速度更快:4个请求同时发起,总时间 ≈ 最慢的那个请求
|
||||
- 🛡️ 容错性强:单个失败不影响其他数据加载
|
||||
- 📊 可统计:知道哪些成功、哪些失败
|
||||
|
||||
### 错误处理
|
||||
|
||||
每个加载任务都有独立的 try-catch:
|
||||
|
||||
```javascript
|
||||
(async () => {
|
||||
try {
|
||||
await fetchData();
|
||||
console.log('✅ 加载成功');
|
||||
} catch (err) {
|
||||
console.error('❌ 加载失败:', err);
|
||||
}
|
||||
})()
|
||||
```
|
||||
|
||||
**优势:**
|
||||
- 单个数据源失败不会中断其他加载
|
||||
- 详细的错误日志便于调试
|
||||
- 应用仍可正常使用(降级处理)
|
||||
|
||||
### 性能监控
|
||||
|
||||
记录加载耗时:
|
||||
|
||||
```javascript
|
||||
const startTime = Date.now();
|
||||
// ... 加载数据 ...
|
||||
const duration = ((Date.now() - startTime) / 1000).toFixed(2);
|
||||
console.log(`[App] 🎉 配置加载完成 (${duration}s)`);
|
||||
```
|
||||
|
||||
**日志输出:**
|
||||
```
|
||||
[App] 🎉 配置加载完成 (1.23s)
|
||||
[App] 📊 成功: 4, 失败: 0
|
||||
```
|
||||
|
||||
## 用户体验提升
|
||||
|
||||
### Before(优化前)
|
||||
|
||||
```
|
||||
用户操作流程:
|
||||
1. 打开应用
|
||||
2. 点击左侧边栏 → API 配置
|
||||
3. 等待加载配置文件
|
||||
4. 选择/创建配置
|
||||
5. 点击左侧边栏 → 预设
|
||||
6. 等待加载预设列表
|
||||
7. 选择预设
|
||||
8. 点击左侧边栏 → 角色卡
|
||||
9. 等待加载角色列表
|
||||
10. 选择角色
|
||||
11. 开始聊天
|
||||
|
||||
总耗时:~5-10秒(多次等待)
|
||||
```
|
||||
|
||||
### After(优化后)
|
||||
|
||||
```
|
||||
用户操作流程:
|
||||
1. 打开应用(后台自动加载所有配置)
|
||||
2. 等待 ~1-2秒(并行加载)
|
||||
3. 直接选择角色开始聊天
|
||||
|
||||
总耗时:~1-2秒(一次等待)
|
||||
```
|
||||
|
||||
**提升:**
|
||||
- ⏱️ 减少等待时间 60-80%
|
||||
- 🎯 操作步骤减少 7步 → 2步
|
||||
- 😊 用户体验更流畅
|
||||
|
||||
## 日志示例
|
||||
|
||||
### 成功情况
|
||||
|
||||
```
|
||||
[App] 🚀 应用启动,开始加载默认配置...
|
||||
[App] ✅ API 配置文件列表加载完成
|
||||
[App] 📝 自动加载第一个配置文件: Default
|
||||
[App] ✅ API 配置详情加载完成
|
||||
[App] ✅ 预设列表加载完成
|
||||
[App] 📝 自动选择第一个预设: Default
|
||||
[App] ✅ 角色卡列表加载完成
|
||||
[App] ✅ 世界书列表加载完成
|
||||
[App] 🎉 配置加载完成 (1.23s)
|
||||
[App] 📊 成功: 4, 失败: 0
|
||||
```
|
||||
|
||||
### 部分失败情况
|
||||
|
||||
```
|
||||
[App] 🚀 应用启动,开始加载默认配置...
|
||||
[App] ✅ API 配置文件列表加载完成
|
||||
[App] ✅ 预设列表加载完成
|
||||
[App] 📝 自动选择第一个预设: Default
|
||||
[App] ✅ 角色卡列表加载完成
|
||||
[App] ❌ 世界书列表加载失败: Network error
|
||||
[App] 🎉 配置加载完成 (2.45s)
|
||||
[App] 📊 成功: 3, 失败: 1
|
||||
[App] ⚠️ 部分配置加载失败,但应用仍可正常使用
|
||||
```
|
||||
|
||||
### 没有配置的情况
|
||||
|
||||
```
|
||||
[App] 🚀 应用启动,开始加载默认配置...
|
||||
[App] ✅ API 配置文件列表加载完成
|
||||
[App] ⚠️ 没有可用的 API 配置文件,请先到 API 配置页面创建
|
||||
[App] ✅ 预设列表加载完成
|
||||
[App] ⚠️ 没有可用的预设
|
||||
[App] ✅ 角色卡列表加载完成
|
||||
[App] ✅ 世界书列表加载完成
|
||||
[App] 🎉 配置加载完成 (0.89s)
|
||||
[App] 📊 成功: 4, 失败: 0
|
||||
```
|
||||
|
||||
## 最佳实践
|
||||
|
||||
### 1. 只预加载必要的数据
|
||||
|
||||
**应该预加载:**
|
||||
- ✅ 应用运行必需的配置
|
||||
- ✅ 用户首次操作就需要看到的数据
|
||||
- ✅ 数据量小、加载快的内容
|
||||
|
||||
**不应该预加载:**
|
||||
- ❌ 查看型数据(统计、历史等)
|
||||
- ❌ 大数据量的内容
|
||||
- ❌ 用户可能不需要的功能
|
||||
|
||||
### 2. 并行而非串行
|
||||
|
||||
```javascript
|
||||
// ❌ 错误:串行加载,总时间 = 所有请求时间之和
|
||||
await loadA();
|
||||
await loadB();
|
||||
await loadC();
|
||||
|
||||
// ✅ 正确:并行加载,总时间 ≈ 最慢的请求
|
||||
await Promise.allSettled([
|
||||
loadA(),
|
||||
loadB(),
|
||||
loadC()
|
||||
]);
|
||||
```
|
||||
|
||||
### 3. 容错处理
|
||||
|
||||
```javascript
|
||||
// ❌ 错误:一个失败全部中断
|
||||
await Promise.all([loadA(), loadB(), loadC()]);
|
||||
|
||||
// ✅ 正确:单个失败不影响其他
|
||||
await Promise.allSettled([loadA(), loadB(), loadC()]);
|
||||
```
|
||||
|
||||
### 4. 详细日志
|
||||
|
||||
```javascript
|
||||
// ❌ 错误:没有日志,无法调试
|
||||
await fetchData();
|
||||
|
||||
// ✅ 正确:清晰的日志输出
|
||||
console.log('[App] 🚀 开始加载...');
|
||||
try {
|
||||
await fetchData();
|
||||
console.log('[App] ✅ 加载成功');
|
||||
} catch (err) {
|
||||
console.error('[App] ❌ 加载失败:', err);
|
||||
}
|
||||
```
|
||||
|
||||
### 5. 性能监控
|
||||
|
||||
```javascript
|
||||
const startTime = Date.now();
|
||||
await loadData();
|
||||
const duration = Date.now() - startTime;
|
||||
console.log(`加载耗时: ${duration}ms`);
|
||||
```
|
||||
|
||||
## 未来优化方向
|
||||
|
||||
### 1. 缓存策略
|
||||
|
||||
```javascript
|
||||
// 检查是否有缓存数据
|
||||
const cached = localStorage.getItem('apiConfig');
|
||||
if (cached) {
|
||||
// 使用缓存,后台静默更新
|
||||
useCache(cached);
|
||||
fetchAndUpdateInBackground();
|
||||
} else {
|
||||
// 没有缓存,正常加载
|
||||
await fetchData();
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 懒加载非关键数据
|
||||
|
||||
```javascript
|
||||
// 关键数据:立即加载
|
||||
await loadCriticalData();
|
||||
|
||||
// 非关键数据:空闲时加载
|
||||
requestIdleCallback(() => {
|
||||
loadNonCriticalData();
|
||||
});
|
||||
```
|
||||
|
||||
### 3. 预取用户可能需要的数据
|
||||
|
||||
```javascript
|
||||
// 用户选择了角色,预取该角色的聊天历史
|
||||
selectCharacter(char => {
|
||||
preloadChatHistory(char.id);
|
||||
});
|
||||
```
|
||||
|
||||
## 总结
|
||||
|
||||
通过应用启动时预加载必要配置:
|
||||
|
||||
✅ **用户体验提升**
|
||||
- 减少等待时间 60-80%
|
||||
- 操作步骤大幅简化
|
||||
- 无需手动切换页面配置
|
||||
|
||||
✅ **技术优势**
|
||||
- 并行加载,速度更快
|
||||
- 容错性强,单个失败不影响整体
|
||||
- 详细日志,便于调试
|
||||
|
||||
✅ **代码质量**
|
||||
- 清晰的加载逻辑
|
||||
- 完善的错误处理
|
||||
- 性能监控和统计
|
||||
|
||||
这种优化让应用更加"开箱即用",用户打开即可开始使用,无需繁琐的初始配置步骤。
|
||||
Reference in New Issue
Block a user