Private
Public Access
1
0

新增:文档体系重构+CHANGELOG补充+发布产物清理

This commit is contained in:
2026-05-01 22:22:06 +08:00
parent 3e1a540b83
commit 6eaaa56eb6
164 changed files with 40346 additions and 64 deletions

View File

@@ -0,0 +1,592 @@
# 版本更新逻辑对比分析
## 📋 整体架构对比
### 原始版本cc50de0- ✅ 组件自治模式
```
UpdatePanel.vue
├── 状态管理:组件内部 ref()
│ ├── downloading (ref)
│ ├── downloadProgress (ref)
│ ├── progressInfo (ref)
│ └── updateInfo (ref)
├── 事件监听:组件内监听
│ ├── EventsOn('download-progress')
│ └── EventsOn('download-complete')
└── API 调用:直接调用后端
├── DownloadUpdate()
└── InstallUpdate()
```
**特点**
- ✅ 组件自包含所有状态
- ✅ 事件监听在组件内部注册
- ✅ 响应性明确ref.value = 触发更新
---
### 当前版本HEAD- ❌ Store 集中模式(已修复)
```
App.vue
├── 事件监听:全局注册
│ └── updateStore.setupEventListeners()
└── 调用updateStore.checkForUpdates(true)
stores/update.ts (Pinia)
├── 状态管理:集中存储
│ ├── downloading (ref)
│ ├── downloadProgress (ref)
│ ├── progressInfo (ref) ← 已修复
│ └── updateInfo (ref)
├── 事件监听:全局监听
│ ├── EventsOn('download-progress')
│ └── EventsOn('download-complete')
└── API 调用:通过 store 调用
├── downloadUpdate()
└── installUpdate()
UpdatePanel.vue
├── 状态获取storeToRefs(store)
│ ├── downloading
│ ├── downloadProgress
│ ├── progressInfo
│ └── updateInfo
├── 事件监听:仅监听 download-complete本地用途
└── API 调用:调用 store 方法
├── updateStore.checkForUpdates(false)
└── updateStore.downloadUpdate()
```
**特点**
- ✅ 状态集中管理
- ✅ 逻辑复用(多处可用)
- ✅ 经过修复后响应性正常
---
## 🔍 详细逻辑对比
### 1. 状态定义
#### 原始版本
```typescript
// ✅ 所有状态都是组件内的 ref
const downloading = ref(false)
const installing = ref(false)
const downloadProgress = ref(0)
const downloadStatus = ref('active')
// ✅ progressInfo 是 ref包含嵌套对象
const progressInfo = ref({
progress: 0,
speed: 0,
downloaded: 0,
total: 0
})
const updateInfo = ref(null)
const downloadedFile = ref(null)
```
#### 当前版本
```typescript
// stores/update.ts
const downloading = ref(false)
const installing = ref(false)
const downloadProgress = ref(0)
const downloadStatus = ref<'active' | 'exception' | 'success'>('active')
// ✅ progressInfo 是 ref修复后
const progressInfo = ref({
speed: 0,
downloaded: 0,
total: 0
})
const updateInfo = ref<UpdateInfo | null>(null)
// UpdatePanel.vue
import { storeToRefs } from 'pinia'
const updateStore = useUpdateStore()
// ✅ 使用 storeToRefs 解构保持响应性
const {
checking,
downloading,
installing,
downloadProgress,
downloadStatus,
progressInfo,
updateInfo
} = storeToRefs(updateStore)
```
---
### 2. 下载流程
#### 原始版本
```typescript
// 步骤 1: 点击下载按钮
const handleDownload = async () => {
if (!updateInfo.value?.download_url) {
Message.warning('下载地址不存在')
return
}
// ✅ 直接设置组件状态
downloading.value = true
downloadProgress.value = 0
downloadStatus.value = 'active'
progressInfo.value = { progress: 0, speed: 0, downloaded: 0, total: 0 }
installResult.value = null
// 调用后端 API
const result = await window.go.main.App.DownloadUpdate(updateInfo.value.download_url)
if (result.success) {
Message.success('下载请求已发送')
}
}
// 步骤 2: 后端发送进度事件
const onDownloadProgress = (event) => {
const data = parseEventData(event)
// ✅ 直接修改 ref触发响应
progressInfo.value = {
progress: data.progress || 0,
speed: data.speed || 0,
downloaded: data.downloaded || 0,
total: data.total || 0
}
downloadProgress.value = Math.round(data.progress || 0)
}
// 步骤 3: 后端发送完成事件
const onDownloadComplete = (event) => {
downloading.value = false
const data = parseEventData(event)
if (data.success) {
downloadStatus.value = 'success'
downloadProgress.value = 100
downloadedFile.value = data.file_path
Message.success('下载完成!文件已保存到:' + data.file_path)
}
}
```
**流程图**
```
用户点击下载
→ handleDownload()
→ downloading.value = true (组件状态)
→ window.go.main.App.DownloadUpdate()
后端发送事件
→ EventsOn('download-progress')
→ onDownloadProgress()
→ progressInfo.value = {...} (✅ 触发更新)
→ downloadProgress.value = 0~100 (✅ 触发更新)
→ EventsOn('download-complete')
→ onDownloadComplete()
→ downloadedFile.value = path (✅ 触发更新)
```
#### 当前版本(修复后)
```typescript
// 步骤 1: 点击下载按钮
const handleDownload = async () => {
// 调用 store 的下载方法
updateStore.downloadUpdate()
}
// stores/update.ts
const downloadUpdate = async () => {
const url = updateInfo.value?.download_url
if (!url) {
Message.warning('下载地址不存在')
return
}
// ✅ 设置 store 状态
downloading.value = true
downloadProgress.value = 0
downloadStatus.value = 'active'
progressInfo.value = { speed: 0, downloaded: 0, total: 0 } // ✅ 修复:替换整个对象
// 调用后端 API
const result = await window.go.main.App.DownloadUpdate(url)
if (result.success) {
Message.success('下载请求已发送')
}
}
// 步骤 2: 后端发送进度事件
// stores/update.ts
const onDownloadProgress = (event: unknown) => {
const now = Date.now()
if (now - lastUpdateTime < UPDATE_THROTTLE) return
lastUpdateTime = now
const data = parseEventData(event)
// ✅ 替换整个 ref 对象(修复后)
progressInfo.value = {
speed: (data.speed as number) || 0,
downloaded: (data.downloaded as number) || 0,
total: (data.total as number) || 0
}
const rawProgress = Number(data.progress) || 0
const safeProgress = Math.min(100, Math.max(0, Math.round(rawProgress)))
downloadProgress.value = safeProgress
}
// 步骤 3: 后端发送完成事件
const onDownloadComplete = (event: unknown) => {
const data = parseEventData(event)
if (data.success) {
downloading.value = false
downloadProgress.value = 100
const fileSize = (data.file_size as number) || 0
// ✅ 替换整个 ref 对象(修复后)
progressInfo.value = {
speed: 0,
downloaded: fileSize,
total: fileSize
}
// 延迟自动安装
setTimeout(() => installUpdate(data.file_path as string), 800)
}
}
```
**流程图**
```
用户点击下载
→ handleDownload()
→ updateStore.downloadUpdate()
Store 更新状态
→ downloading.value = true (store 状态)
→ downloadProgress.value = 0 (store 状态)
→ progressInfo.value = {...} (✅ 触发更新 - 修复后)
后端发送事件
→ App.vue: EventsOn('download-progress')
→ store.onDownloadProgress()
→ progressInfo.value = {...} (✅ 触发更新 - 修复后)
→ downloadProgress.value = 0~100 (✅ 触发更新)
→ App.vue: EventsOn('download-complete')
→ store.onDownloadComplete()
→ downloading.value = false (✅ 触发更新)
→ progressInfo.value = {...} (✅ 触发更新 - 修复后)
→ 延迟调用 installUpdate()
UpdatePanel 组件
→ storeToRefs 解构 store
→ progressInfo (Ref<Ref<...>>)
→ 模板中自动响应变化 (✅ 正常工作)
```
---
### 3. 事件监听注册
#### 原始版本
```typescript
// UpdatePanel.vue - 组件内部监听
onMounted(async () => {
await loadCurrentVersion()
await loadConfig()
// ✅ 组件内监听事件
window.EventsOn('download-progress', onDownloadProgress)
window.EventsOn('download-complete', onDownloadComplete)
})
onUnmounted(() => {
// ✅ 组件卸载时清理
window.EventsOff('download-progress')
window.EventsOff('download-complete')
})
```
**特点**
- ✅ 组件自包含
- ✅ 事件监听器生命周期与组件同步
- ✅ 组件卸载时自动清理
#### 当前版本
```typescript
// App.vue - 应用启动时全局监听
onMounted(() => {
loadConfig()
// ✅ 全局注册事件监听(一次)
updateStore.setupEventListeners()
// 延迟检查更新
setTimeout(() => {
updateStore.checkForUpdates(true) // 静默模式
}, 3000)
})
onUnmounted(() => {
// ✅ 应用卸载时清理(一次)
updateStore.removeEventListeners()
})
// stores/update.ts
const setupEventListeners = () => {
if (!window.runtime?.EventsOn) return
window.runtime.EventsOn('download-progress', onDownloadProgress)
window.runtime.EventsOn('download-complete', onDownloadComplete)
}
const removeEventListeners = () => {
if (!window.runtime?.EventsOff) return
window.runtime.EventsOff('download-progress')
window.runtime.EventsOff('download-complete)
}
// UpdatePanel.vue - 仅监听 download-complete本地用途
onMounted(async () => {
await loadCurrentVersion()
await loadConfig()
// 仅监听 download-complete 用于记录文件路径
if (window.runtime?.EventsOn) {
window.runtime.EventsOn('download-complete', onDownloadComplete)
}
})
onUnmounted(() => {
if (window.runtime?.EventsOff) {
window.runtime.EventsOff('download-complete')
}
// 清理定时器
if (saveTimer) {
clearTimeout(saveTimer)
}
})
```
**特点**
- ✅ 全局唯一事件监听(避免重复)
- ✅ 状态集中管理
- ✅ 生命周期清晰App 级别)
---
### 4. 响应性更新
#### 原始版本 - ✅ 直接赋值
```typescript
// progressInfo 是 ref
const progressInfo = ref({ speed: 0, downloaded: 0, total: 0 })
// ✅ 直接替换对象Vue 能检测到变化
progressInfo.value = {
progress: data.progress || 0,
speed: data.speed || 0,
downloaded: data.downloaded || 0,
total: data.total || 0
}
// ✅ 下载进度 (0-100)
downloadProgress.value = Math.round(data.progress || 0)
```
**Vue 2/3 响应式原理**
```
ref.value = newValue
→ Vue setter 被调用
→ 触发依赖追踪
→ 重新渲染组件
```
#### 当前版本(修复前)- ❌ Object.assign
```typescript
// ❌ 错误progressInfo 是 reactive
const progressInfo = reactive({
speed: 0,
downloaded: 0,
total: 0
})
// ❌ Object.assign 修改属性Vue 检测不到变化
Object.assign(progressInfo, {
speed: data.speed || 0,
downloaded: data.downloaded || 0,
total: data.total || 0
})
// ❌ 问题Vue 3 中 reactive 对象的属性修改不会触发 setter
```
**Vue 3 reactive 响应式原理**
```
reactive(obj)
→ 返回 Proxy 对象
→ property set = 触发 trap
→ 需要使用 toRaw() 解包才能比较
// ❌ Object.assign 的问题:
// - Object.assign 在 Proxy 上可能不工作
// - 即使工作Vue 3 的响应式系统可能检测不到嵌套属性的变化
```
#### 当前版本(修复后)- ✅ 替换对象
```typescript
// ✅ 正确progressInfo 是 ref
const progressInfo = ref({
speed: 0,
downloaded: 0,
total: 0
})
// ✅ 替换整个对象Vue 能检测到变化
progressInfo.value = {
speed: (data.speed as number) || 0,
downloaded: (data.downloaded as number) || 0,
total: (data.total as number) || 0
}
// ✅ 下载进度 (0-100)
downloadProgress.value = Math.min(100, Math.max(0, Math.round(rawProgress)))
```
**修复原理**
```
ref.value = newValue
→ Vue setter 被调用
→ 触发依赖追踪
→ 重新渲染组件
storeToRefs(store)
→ 将 store.state 的每个属性转换为 Ref
→ progressInfo = RefImpl<Ref<{speed, downloaded, total}>>
→ progressInfo.value 调用会触发响应式更新
```
---
## 🎯 核心差异总结
### 架构模式
| 维度 | 原始版本 | 当前版本 |
|------|---------|---------|
| **状态管理** | 组件内部(分散) | Store 集中(统一) |
| **事件监听** | 组件内监听 | 全局监听 |
| **API 调用** | 组件直接调用 | Store 方法调用 |
| **生命周期** | 组件级别 | 应用级别 |
### 响应性
| 状态类型 | 原始版本 | 当前版本(修复前) | 当前版本(修复后) |
|---------|---------|----------------|----------------|
| **progressInfo** | `ref({...})` ✅ | `reactive({...})` ❌ | `ref({...})` ✅ |
| **更新方式** | `.value = {...}` ✅ | `Object.assign()` ❌ | `.value = {...}` ✅ |
| **响应性** | ✅ 正常 | ❌ 断裂 | ✅ 正常 |
### 代码质量
| 指标 | 原始版本 | 当前版本 |
|------|---------|---------|
| **代码重复** | 有(每个组件独立) | 无store 复用) |
| **逻辑复用** | 无 | 有 |
| **类型安全** | 部分 | 完整 |
| **维护性** | 中 | 高 |
---
## ✅ 修复确认
### 修复的文件
**stores/update.ts**
1. progressInfo: `reactive({...})``ref({...})`
2. onDownloadProgress: `Object.assign()``.value = {}`
3. onDownloadComplete: `Object.assign()``.value = {}`
4. downloadUpdate: `Object.assign()``.value = {}`
### 验证结果
-**构建成功**55.10s
-**响应性恢复**ref + storeToRefs
-**进度显示**0-100% 实时更新
-**文件大小显示**:已下载 / 总大小
-**下载速度显示**XX KB/s / MB/s
---
## 🎓 经验总结
### reactive vs ref 的选择
**使用 reactive**
- ✅ 顶层状态对象
- ✅ 不需要替换整个对象
- ✅ 只修改属性值
**使用 ref**
- ✅ 需要替换整个对象(如 progressInfo
- ✅ 基础类型number, string, boolean
- ✅ 需要明确重新赋值
### Pinia Store 最佳实践
1. **状态定义**
- 简单值:使用 `ref()`
- 对象:根据需求选择 `ref()``reactive()`
2. **组件使用**
- 必须使用 `storeToRefs()` 解构
- 不要用 `computed()` 包装 store 状态
3. **更新方式**
- ref: `.value = newValue`
- reactive: `obj.property = newValue``Object.assign(obj, {...})`
---
## 📊 对比结论
### 架构升级
**原始版本****当前版本**
- ✅ 分散 → 集中
- ✅ 无复用 → 可复用
- ✅ 无类型 → 完整类型
- ⚠️ 需要注意响应性问题
### 关键修复
`progressInfo``reactive` 改为 `ref`,使用 `.value = {}` 替换整个对象,确保响应性更新。
---
**文档创建时间**2026-02-04
**对比版本**cc50de0原始 vs HEAD当前
**状态**:✅ 已修复,正常显示进度