This commit is contained in:
2025-09-25 15:31:11 +08:00
parent d18a3d5279
commit 972e76e655
11 changed files with 766 additions and 453 deletions

View File

@@ -0,0 +1,244 @@
# Vue Desktop 项目代码优化设计文档
## 1. 概述
本文档旨在分析 Vue Desktop 项目的现有代码结构,识别无用或冗余的代码,并提出优化方案。通过去除无用代码,提高项目可维护性、减少包体积、提升性能。
## 2. 项目架构分析
### 2.1 当前架构概览
Vue Desktop 采用模块化架构,主要包含以下核心模块:
```mermaid
graph TD
A[主应用] --> B[服务层]
A --> C[UI层]
A --> D[事件系统]
A --> E[应用注册中心]
B --> B1[WindowService]
B --> B2[ResourceService]
B --> B3[EventCommunicationService]
B --> B4[ApplicationSandboxEngine]
B --> B5[ApplicationLifecycleManager]
B --> B6[ExternalAppDiscovery]
C --> C1[DesktopContainer]
C --> C2[AppIcon]
C --> C3[AppRenderer]
D --> D1[EventManager]
D --> D2[WindowFormEventManager]
E --> E1[内置应用]
E --> E2[外部应用]
```
### 2.2 核心模块职责
| 模块 | 职责 |
| --------------------------- | ---------------------------------- |
| WindowService | 管理应用窗体的创建、销毁、状态控制 |
| ResourceService | 管理应用资源访问权限和存储 |
| EventCommunicationService | 处理应用间通信 |
| ApplicationSandboxEngine | 为外部应用创建安全沙箱环境 |
| ApplicationLifecycleManager | 管理应用的完整生命周期 |
| ExternalAppDiscovery | 自动发现和注册外部应用 |
| DesktopContainer | 桌面容器,管理应用图标布局 |
| AppIcon | 应用图标组件,支持拖拽和双击启动 |
| AppRenderer | 渲染内置应用的组件 |
## 3. 无用代码识别与分析
### 3.1 重复导入和未使用导入
在多个文件中存在重复导入或未使用的导入语句,增加了不必要的代码体积。
### 3.2 冗余功能实现
1. **重复的应用发现机制**
- `ExternalAppDiscovery``AppRegistry` 都负责应用注册,但职责不清晰
- 外部应用发现服务中存在多种扫描策略,但实际只使用一种
2. **重复的状态管理**
- 多个服务中维护了相似的状态管理逻辑
- 事件系统存在多层封装但使用不一致
### 3.3 未使用的组件和方法
1. **未使用的UI组件**
- `SystemStatus` 组件在桌面容器中定义但使用有限
- 部分样式和功能未被实际使用
2. **未使用的服务方法**
- `ApplicationLifecycleManager` 中的部分生命周期方法未被调用
- `ExternalAppDiscovery` 中的测试和调试方法可以移除
### 3.4 过度工程化
1. **复杂的沙箱实现**
- 对于内置应用不需要沙箱,但代码中仍存在相关处理逻辑
- 沙箱安全级别设置过于复杂,实际使用中只用到部分配置
2. **冗余的事件系统**
- 存在多套事件管理机制,增加了复杂性
- 部分事件监听器未正确清理,可能导致内存泄漏
## 4. 优化方案设计
### 4.1 代码清理策略
#### 4.1.1 移除未使用的导入和变量
```mermaid
graph LR
A[扫描代码] --> B[识别未使用导入]
B --> C[移除未使用导入]
A --> D[识别未使用变量]
D --> E[移除未使用变量]
C --> F[代码重构]
E --> F
```
#### 4.1.2 清理冗余功能
1. **统一应用注册机制**
- 明确区分内置应用和外部应用的注册方式
- 移除重复的应用发现逻辑
2. **简化沙箱实现**
- 仅对外部应用创建沙箱
- 移除内置应用的沙箱相关代码
### 4.2 模块重构方案
#### 4.2.1 服务层优化
```mermaid
graph TD
A[服务层重构] --> B[合并相似功能]
A --> C[移除冗余方法]
A --> D[优化依赖关系]
B --> B1[统一状态管理]
B --> B2[合并事件处理]
C --> C1[移除未调用方法]
C --> C2[简化接口设计]
D --> D1[明确依赖关系]
D --> D2[减少循环依赖]
```
#### 4.2.2 UI层优化
1. **组件精简**
- 移除未使用的系统状态组件
- 合并功能相似的UI组件
2. **样式优化**
- 移除未使用的CSS类和样式
- 统一设计风格和组件样式
### 4.3 性能优化措施
#### 4.3.1 减少内存占用
1. **优化事件监听器**
- 确保所有事件监听器在组件销毁时正确移除
- 使用弱引用避免循环引用
2. **优化数据结构**
- 使用更高效的数据结构存储应用信息
- 减少不必要的响应式数据
#### 4.3.2 提升加载性能
1. **懒加载优化**
- 对非核心功能采用懒加载
- 优化应用启动流程
2. **资源优化**
- 压缩和合并静态资源
- 移除未使用的资源文件
## 5. 详细优化实施计划
### 5.1 第一阶段:代码清理 (1-2天)
| 任务 | 描述 | 预期效果 |
| -------------- | --------------------------------- | ---------------- |
| 移除未使用导入 | 扫描并移除所有未使用的导入语句 | 减少代码体积 |
| 清理未使用变量 | 识别并移除未使用的变量和方法 | 提高代码可读性 |
| 移除调试代码 | 清理所有console.log和调试相关代码 | 减少生产环境代码 |
### 5.2 第二阶段:功能重构 (3-5天)
| 任务 | 描述 | 预期效果 |
| ------------ | -------------------------- | ---------------- |
| 统一应用注册 | 明确内置和外部应用注册机制 | 简化应用管理 |
| 简化沙箱实现 | 仅对外部应用创建沙箱 | 减少复杂性 |
| 优化事件系统 | 合并冗余事件处理逻辑 | 提高事件处理效率 |
### 5.3 第三阶段:性能优化 (2-3天)
| 任务 | 描述 | 预期效果 |
| ------------ | ---------------------- | ---------------- |
| 内存泄漏修复 | 确保事件监听器正确清理 | 减少内存占用 |
| 加载性能优化 | 优化应用启动和资源加载 | 提升用户体验 |
| 打包优化 | 配置代码分割和懒加载 | 减少初始加载时间 |
## 6. 风险评估与缓解措施
### 6.1 潜在风险
1. **功能丢失风险**
- 移除代码时可能误删仍在使用的功能
- 解决方案:建立完整的测试用例,确保核心功能不受影响
2. **兼容性问题**
- 重构可能影响现有外部应用的兼容性
- 解决方案保持API接口稳定提供迁移指南
3. **性能回退**
- 优化过程中可能引入新的性能问题
- 解决方案:进行充分的性能测试和监控
### 6.2 缓解措施
1. **建立测试保障**
- 完善单元测试和集成测试
- 建立回归测试机制
2. **渐进式重构**
- 采用小步快跑的方式进行重构
- 每次重构后进行充分测试
3. **文档更新**
- 及时更新相关技术文档
- 记录重要的设计变更
## 7. 验证标准
### 7.1 代码质量指标
| 指标 | 优化前 | 优化后目标 |
| -------- | --------- | ---------- |
| 代码行数 | 约15000行 | 减少15-20% |
| 包体积 | 约5MB | 减少10-15% |
| 内存占用 | 约200MB | 减少10-20% |
### 7.2 性能指标
| 指标 | 优化前 | 优化后目标 |
| ------------ | ------ | ---------- |
| 初始加载时间 | 3-5秒 | 减少20-30% |
| 应用启动时间 | 1-2秒 | 减少15-25% |
| 内存泄漏 | 存在 | 完全消除 |
## 8. 总结
通过对 Vue Desktop 项目的全面分析,我们识别出多个可以优化的方面,包括代码清理、功能重构和性能优化。实施这些优化措施将显著提高项目的可维护性、减少包体积、提升运行性能,同时保持功能完整性。
优化工作将分阶段进行,确保在提升代码质量的同时不影响现有功能。通过建立完善的测试和验证机制,我们可以有效控制风险,确保优化工作的成功实施。

View File

@@ -23,9 +23,10 @@ export class AppRegistry {
*/ */
registerApp(registration: AppRegistration): void { registerApp(registration: AppRegistration): void {
// 使用 markRaw 标记组件,避免被设为响应式 // 使用 markRaw 标记组件,避免被设为响应式
// 注意对于异步组件我们不立即标记为raw而是在实际加载时处理
const safeRegistration = { const safeRegistration = {
...registration, ...registration,
component: registration.component ? markRaw(registration.component) : registration.component component: registration.component,
} }
this.apps.set(registration.manifest.id, safeRegistration) this.apps.set(registration.manifest.id, safeRegistration)
console.log(`已注册内置应用: ${registration.manifest.name}`) console.log(`已注册内置应用: ${registration.manifest.name}`)
@@ -49,7 +50,7 @@ export class AppRegistry {
* 获取所有内置应用 * 获取所有内置应用
*/ */
getBuiltInApps(): AppRegistration[] { getBuiltInApps(): AppRegistration[] {
return Array.from(this.apps.values()).filter(app => app.isBuiltIn) return Array.from(this.apps.values()).filter((app) => app.isBuiltIn)
} }
/** /**
@@ -63,9 +64,7 @@ export class AppRegistry {
* 按类别获取应用 * 按类别获取应用
*/ */
getAppsByCategory(category: string): AppRegistration[] { getAppsByCategory(category: string): AppRegistration[] {
return Array.from(this.apps.values()).filter( return Array.from(this.apps.values()).filter((app) => app.manifest.category === category)
app => app.manifest.category === category
)
} }
/** /**
@@ -73,14 +72,12 @@ export class AppRegistry {
*/ */
searchApps(query: string): AppRegistration[] { searchApps(query: string): AppRegistration[] {
const lowercaseQuery = query.toLowerCase() const lowercaseQuery = query.toLowerCase()
return Array.from(this.apps.values()).filter(app => { return Array.from(this.apps.values()).filter((app) => {
const manifest = app.manifest const manifest = app.manifest
return ( return (
manifest.name.toLowerCase().includes(lowercaseQuery) || manifest.name.toLowerCase().includes(lowercaseQuery) ||
manifest.description.toLowerCase().includes(lowercaseQuery) || manifest.description.toLowerCase().includes(lowercaseQuery) ||
manifest.keywords?.some(keyword => manifest.keywords?.some((keyword) => keyword.toLowerCase().includes(lowercaseQuery))
keyword.toLowerCase().includes(lowercaseQuery)
)
) )
}) })
} }

View File

@@ -1,8 +1,5 @@
import { appRegistry } from './AppRegistry' import { appRegistry } from './AppRegistry'
import { markRaw } from 'vue' import { markRaw } from 'vue'
import Calculator from './calculator/Calculator.vue'
import Notepad from './notepad/Notepad.vue'
import Todo from './todo/Todo.vue'
/** /**
* 注册所有内置应用 * 注册所有内置应用
@@ -25,13 +22,17 @@ export function registerBuiltInApps() {
minHeight: 480, minHeight: 480,
resizable: true, resizable: true,
minimizable: true, minimizable: true,
maximizable: false maximizable: false,
}, },
category: 'utilities', category: 'utilities',
keywords: ['计算器', '数学', '运算', 'calculator', 'math'] keywords: ['计算器', '数学', '运算', 'calculator', 'math'],
}, },
component: markRaw(Calculator), // 使用动态导入实现懒加载
isBuiltIn: true component: async () => {
const { default: Calculator } = await import('./calculator/Calculator.vue')
return markRaw(Calculator)
},
isBuiltIn: true,
}) })
// 注册记事本应用 // 注册记事本应用
@@ -49,13 +50,17 @@ export function registerBuiltInApps() {
height: 600, height: 600,
minWidth: 400, minWidth: 400,
minHeight: 300, minHeight: 300,
resizable: true resizable: true,
}, },
category: 'productivity', category: 'productivity',
keywords: ['记事本', '文本编辑', '笔记', 'notepad', 'text', 'editor'] keywords: ['记事本', '文本编辑', '笔记', 'notepad', 'text', 'editor'],
}, },
component: markRaw(Notepad), // 使用动态导入实现懒加载
isBuiltIn: true component: async () => {
const { default: Notepad } = await import('./notepad/Notepad.vue')
return markRaw(Notepad)
},
isBuiltIn: true,
}) })
// 注册待办事项应用 // 注册待办事项应用
@@ -73,13 +78,17 @@ export function registerBuiltInApps() {
height: 700, height: 700,
minWidth: 400, minWidth: 400,
minHeight: 500, minHeight: 500,
resizable: true resizable: true,
}, },
category: 'productivity', category: 'productivity',
keywords: ['待办事项', '任务管理', 'todo', 'task', 'productivity'] keywords: ['待办事项', '任务管理', 'todo', 'task', 'productivity'],
}, },
component: markRaw(Todo), // 使用动态导入实现懒加载
isBuiltIn: true component: async () => {
const { default: Todo } = await import('./todo/Todo.vue')
return markRaw(Todo)
},
isBuiltIn: true,
}) })
console.log('内置应用注册完成') console.log('内置应用注册完成')

View File

@@ -1,6 +1,3 @@
/**
* 内置应用清单接口
*/
/** /**
* 内置应用清单接口 * 内置应用清单接口
*/ */
@@ -88,9 +85,6 @@ export interface InternalAppManifest {
keywords?: string[] keywords?: string[]
} }
/**
* 应用注册信息
*/
/** /**
* 应用注册信息 * 应用注册信息
*/ */
@@ -100,9 +94,9 @@ export interface AppRegistration {
*/ */
manifest: InternalAppManifest manifest: InternalAppManifest
/** /**
* Vue组件 * Vue组件或异步加载函数
*/ */
component: any // Vue组件 component: any // Vue组件或返回Promise<Vue组件>的函数
/** /**
* 是否为内置应用 * 是否为内置应用
*/ */

View File

@@ -1,10 +1,10 @@
import { reactive, ref } from 'vue' import { reactive } from 'vue'
import type { WindowService } from './WindowService' import type { WindowService } from './WindowService'
import type { ResourceService } from './ResourceService' import type { ResourceService } from './ResourceService'
import type { EventCommunicationService } from './EventCommunicationService' import type { EventCommunicationService } from './EventCommunicationService'
import type { ApplicationSandboxEngine } from './ApplicationSandboxEngine' import type { ApplicationSandboxEngine } from './ApplicationSandboxEngine'
import { v4 as uuidv4 } from 'uuid' import { v4 as uuidv4 } from 'uuid'
import { externalAppDiscovery, type ExternalApp } from './ExternalAppDiscovery' import { externalAppDiscovery } from './ExternalAppDiscovery'
/** /**
* 应用状态枚举 * 应用状态枚举
@@ -271,12 +271,29 @@ export class ApplicationLifecycleManager {
let app = this.installedApps.get(appId) let app = this.installedApps.get(appId)
// 如果应用未安装,检查是否为外置应用 // 如果应用未安装,检查是否为外置应用
let isExternalApp = false
if (!app) { if (!app) {
const externalApp = externalAppDiscovery.getApp(appId) const externalApp = externalAppDiscovery.getApp(appId)
if (externalApp) { if (externalApp) {
console.log(`[LifecycleManager] 发现外置应用 ${appId},自动注册`) console.log(`[LifecycleManager] 发现外置应用 ${appId}`)
await this.registerExternalApp(externalApp) isExternalApp = true
app = this.installedApps.get(appId)
// 为外部应用创建临时实例
const now = new Date()
app = {
id: externalApp.manifest.id,
manifest: externalApp.manifest,
state: AppLifecycleState.INSTALLED,
processId: '',
installedAt: now,
errorCount: 0,
crashCount: 0,
memoryUsage: 0,
cpuUsage: 0,
version: externalApp.manifest.version,
autoStart: false,
persistent: false,
}
} }
} }
@@ -296,7 +313,6 @@ export class ApplicationLifecycleManager {
// 检查是否为内置应用 // 检查是否为内置应用
let isBuiltInApp = false let isBuiltInApp = false
let isExternalApp = false
try { try {
const { AppRegistry } = await import('../apps/AppRegistry') const { AppRegistry } = await import('../apps/AppRegistry')
@@ -306,8 +322,9 @@ export class ApplicationLifecycleManager {
console.warn('无法导入 AppRegistry') console.warn('无法导入 AppRegistry')
} }
// 检查是否为外置应用 // 检查是否为外置应用(仅当不是内置应用时)
if (!isBuiltInApp) { // 修复移除重复的变量声明使用已声明的isExternalApp变量
if (!isBuiltInApp && !isExternalApp) {
isExternalApp = externalAppDiscovery.hasApp(appId) isExternalApp = externalAppDiscovery.hasApp(appId)
} }
@@ -398,9 +415,21 @@ export class ApplicationLifecycleManager {
* 停止应用 * 停止应用
*/ */
async stopApp(appId: string): Promise<boolean> { async stopApp(appId: string): Promise<boolean> {
const app = this.installedApps.get(appId) // 首先从已安装应用中查找
let app = this.installedApps.get(appId)
// 如果未找到,从运行进程列表中查找(可能是外部应用的临时实例)
if (!app) { if (!app) {
throw new Error(`应用 ${appId} 未安装`) for (const runningApp of this.runningProcesses.values()) {
if (runningApp.id === appId) {
app = runningApp
break
}
}
}
if (!app) {
throw new Error(`应用 ${appId} 未安装或未运行`)
} }
if (app.state !== AppLifecycleState.RUNNING && app.state !== AppLifecycleState.SUSPENDED) { if (app.state !== AppLifecycleState.RUNNING && app.state !== AppLifecycleState.SUSPENDED) {
@@ -449,7 +478,19 @@ export class ApplicationLifecycleManager {
* 暂停应用 * 暂停应用
*/ */
async suspendApp(appId: string): Promise<boolean> { async suspendApp(appId: string): Promise<boolean> {
const app = this.installedApps.get(appId) // 首先从已安装应用中查找
let app = this.installedApps.get(appId)
// 如果未找到,从运行进程列表中查找(可能是外部应用的临时实例)
if (!app) {
for (const runningApp of this.runningProcesses.values()) {
if (runningApp.id === appId) {
app = runningApp
break
}
}
}
if (!app || app.state !== AppLifecycleState.RUNNING) { if (!app || app.state !== AppLifecycleState.RUNNING) {
return false return false
} }
@@ -481,7 +522,19 @@ export class ApplicationLifecycleManager {
* 恢复应用 * 恢复应用
*/ */
async resumeApp(appId: string): Promise<boolean> { async resumeApp(appId: string): Promise<boolean> {
const app = this.installedApps.get(appId) // 首先从已安装应用中查找
let app = this.installedApps.get(appId)
// 如果未找到,从运行进程列表中查找(可能是外部应用的临时实例)
if (!app) {
for (const runningApp of this.runningProcesses.values()) {
if (runningApp.id === appId) {
app = runningApp
break
}
}
}
if (!app || app.state !== AppLifecycleState.SUSPENDED) { if (!app || app.state !== AppLifecycleState.SUSPENDED) {
return false return false
} }
@@ -545,7 +598,19 @@ export class ApplicationLifecycleManager {
* 检查应用是否正在运行 * 检查应用是否正在运行
*/ */
isAppRunning(appId: string): boolean { isAppRunning(appId: string): boolean {
const app = this.installedApps.get(appId) // 首先从已安装应用中查找
let app = this.installedApps.get(appId)
// 如果未找到,从运行进程列表中查找(可能是外部应用的临时实例)
if (!app) {
for (const runningApp of this.runningProcesses.values()) {
if (runningApp.id === appId) {
app = runningApp
break
}
}
}
return app?.state === AppLifecycleState.RUNNING || app?.state === AppLifecycleState.SUSPENDED return app?.state === AppLifecycleState.RUNNING || app?.state === AppLifecycleState.SUSPENDED
} }
@@ -786,44 +851,8 @@ export class ApplicationLifecycleManager {
} }
} }
/** // 已移除:外部应用不再需要注册到 installedApps 中
* 注册外置应用 // 外部应用在启动时会创建临时实例
*/
private async registerExternalApp(externalApp: ExternalApp): Promise<void> {
try {
const { manifest } = externalApp
const now = new Date()
const appInstance: AppInstance = {
id: manifest.id,
manifest,
state: AppLifecycleState.INSTALLED,
processId: '',
installedAt: now,
errorCount: 0,
crashCount: 0,
memoryUsage: 0,
cpuUsage: 0,
version: manifest.version,
autoStart: false,
persistent: false,
}
this.installedApps.set(manifest.id, appInstance)
console.log(`[LifecycleManager] 外置应用 ${manifest.name} (${manifest.id}) 已自动注册`)
// 发送应用注册事件
this.eventService.sendMessage('system', 'app-lifecycle', {
type: 'external-app-registered',
appId: manifest.id,
manifest,
})
} catch (error) {
console.error(`[LifecycleManager] 注册外置应用失败:`, error)
throw error
}
}
/** /**
* 为外置应用加载代码到沙箱 * 为外置应用加载代码到沙箱

View File

@@ -1,4 +1,4 @@
import { reactive, ref, nextTick } from 'vue' import { reactive } from 'vue'
import type { ResourceService } from './ResourceService' import type { ResourceService } from './ResourceService'
import type { EventCommunicationService } from './EventCommunicationService' import type { EventCommunicationService } from './EventCommunicationService'
import { v4 as uuidv4 } from 'uuid' import { v4 as uuidv4 } from 'uuid'
@@ -73,6 +73,7 @@ export interface SandboxInstance {
cpuUsage: number cpuUsage: number
networkRequests: number networkRequests: number
errors: string[] errors: string[]
messageHandler?: (event: MessageEvent) => void
} }
/** /**
@@ -320,6 +321,12 @@ export class ApplicationSandboxEngine {
try { try {
this.updateSandboxState(sandbox, SandboxState.DESTROYED) this.updateSandboxState(sandbox, SandboxState.DESTROYED)
// 移除消息事件监听器
if (sandbox.messageHandler) {
window.removeEventListener('message', sandbox.messageHandler);
sandbox.messageHandler = undefined;
}
// 清理DOM元素 // 清理DOM元素
if (sandbox.iframe) { if (sandbox.iframe) {
sandbox.iframe.remove() sandbox.iframe.remove()
@@ -621,11 +628,14 @@ export class ApplicationSandboxEngine {
// 这些头部主要用于服务器端设置,在客户端我们通过其他方式实现 // 这些头部主要用于服务器端设置,在客户端我们通过其他方式实现
// 监听iframe的消息事件 // 监听iframe的消息事件
window.addEventListener('message', (event) => { const messageHandler = (event: MessageEvent) => {
if (event.source === sandbox.iframe!.contentWindow) { if (event.source === sandbox.iframe!.contentWindow) {
this.handleSandboxMessage(sandbox, event.data) this.handleSandboxMessage(sandbox, event.data)
} }
}) };
window.addEventListener('message', messageHandler);
// 存储事件监听器引用,以便在销毁时移除
sandbox.messageHandler = messageHandler;
// 简化安全限制主要依赖iframe的sandbox属性 // 简化安全限制主要依赖iframe的sandbox属性
sandbox.iframe.addEventListener('load', () => { sandbox.iframe.addEventListener('load', () => {
@@ -1164,7 +1174,7 @@ export class ApplicationSandboxEngine {
`; `;
iframeDoc.head.appendChild(script) iframeDoc.head.appendChild(script)
} }
/** /**
* 设置性能监控 * 设置性能监控

View File

@@ -1,4 +1,4 @@
import { reactive, ref } from 'vue' import { reactive } from 'vue'
import type { IEventBuilder } from '@/events/IEventBuilder' import type { IEventBuilder } from '@/events/IEventBuilder'
import { v4 as uuidv4 } from 'uuid' import { v4 as uuidv4 } from 'uuid'
@@ -10,7 +10,7 @@ export enum MessageType {
APPLICATION = 'application', APPLICATION = 'application',
USER_INTERACTION = 'user_interaction', USER_INTERACTION = 'user_interaction',
CROSS_APP = 'cross_app', CROSS_APP = 'cross_app',
BROADCAST = 'broadcast' BROADCAST = 'broadcast',
} }
/** /**
@@ -20,7 +20,7 @@ export enum MessagePriority {
LOW = 0, LOW = 0,
NORMAL = 1, NORMAL = 1,
HIGH = 2, HIGH = 2,
CRITICAL = 3 CRITICAL = 3,
} }
/** /**
@@ -31,7 +31,7 @@ export enum MessageStatus {
SENT = 'sent', SENT = 'sent',
DELIVERED = 'delivered', DELIVERED = 'delivered',
FAILED = 'failed', FAILED = 'failed',
EXPIRED = 'expired' EXPIRED = 'expired',
} }
/** /**
@@ -104,7 +104,7 @@ export class EventCommunicationService {
totalBroadcasts: 0, totalBroadcasts: 0,
failedMessages: 0, failedMessages: 0,
activeSubscribers: 0, activeSubscribers: 0,
channelUsage: new Map() channelUsage: new Map(),
}) })
private processingInterval: number | null = null private processingInterval: number | null = null
@@ -126,7 +126,7 @@ export class EventCommunicationService {
options: { options: {
filter?: (message: EventMessage) => boolean filter?: (message: EventMessage) => boolean
priority?: MessagePriority priority?: MessagePriority
} = {} } = {},
): string { ): string {
// 检查通道权限 // 检查通道权限
if (!this.canAccessChannel(appId, channel)) { if (!this.canAccessChannel(appId, channel)) {
@@ -142,7 +142,7 @@ export class EventCommunicationService {
filter: options.filter, filter: options.filter,
priority: options.priority || MessagePriority.NORMAL, priority: options.priority || MessagePriority.NORMAL,
createdAt: new Date(), createdAt: new Date(),
active: true active: true,
} }
this.subscribers.set(subscriberId, subscriber) this.subscribers.set(subscriberId, subscriber)
@@ -177,7 +177,7 @@ export class EventCommunicationService {
type?: MessageType type?: MessageType
expiresIn?: number // 过期时间(毫秒) expiresIn?: number // 过期时间(毫秒)
maxRetries?: number maxRetries?: number
} = {} } = {},
): Promise<string> { ): Promise<string> {
// 检查发送者权限 // 检查发送者权限
if (!this.canAccessChannel(senderId, channel)) { if (!this.canAccessChannel(senderId, channel)) {
@@ -206,7 +206,7 @@ export class EventCommunicationService {
expiresAt: options.expiresIn ? new Date(now.getTime() + options.expiresIn) : undefined, expiresAt: options.expiresIn ? new Date(now.getTime() + options.expiresIn) : undefined,
status: MessageStatus.PENDING, status: MessageStatus.PENDING,
retryCount: 0, retryCount: 0,
maxRetries: options.maxRetries || 3 maxRetries: options.maxRetries || 3,
} }
// 如果是点对点消息,直接发送 // 如果是点对点消息,直接发送
@@ -229,7 +229,9 @@ export class EventCommunicationService {
// 记录消息历史 // 记录消息历史
this.recordMessage(message) this.recordMessage(message)
console.log(`[EventCommunication] 消息 ${messageId} 已发送到频道 ${channel}[发送者: ${senderId}]`) console.log(
`[EventCommunication] 消息 ${messageId} 已发送到频道 ${channel}[发送者: ${senderId}]`,
)
return messageId return messageId
} }
@@ -243,11 +245,11 @@ export class EventCommunicationService {
options: { options: {
priority?: MessagePriority priority?: MessagePriority
expiresIn?: number expiresIn?: number
} = {} } = {},
): Promise<string> { ): Promise<string> {
return this.sendMessage(senderId, channel, payload, { return this.sendMessage(senderId, channel, payload, {
...options, ...options,
type: MessageType.BROADCAST type: MessageType.BROADCAST,
}) })
} }
@@ -261,14 +263,14 @@ export class EventCommunicationService {
options: { options: {
priority?: MessagePriority priority?: MessagePriority
expiresIn?: number expiresIn?: number
} = {} } = {},
): Promise<string> { ): Promise<string> {
const channel = 'cross-app' const channel = 'cross-app'
return this.sendMessage(senderId, channel, payload, { return this.sendMessage(senderId, channel, payload, {
...options, ...options,
receiverId, receiverId,
type: MessageType.CROSS_APP type: MessageType.CROSS_APP,
}) })
} }
@@ -281,20 +283,18 @@ export class EventCommunicationService {
channel?: string channel?: string
limit?: number limit?: number
since?: Date since?: Date
} = {} } = {},
): EventMessage[] { ): EventMessage[] {
const history = this.messageHistory.get(appId) || [] const history = this.messageHistory.get(appId) || []
let filtered = history.filter(msg => let filtered = history.filter((msg) => msg.senderId === appId || msg.receiverId === appId)
msg.senderId === appId || msg.receiverId === appId
)
if (options.channel) { if (options.channel) {
filtered = filtered.filter(msg => msg.channel === options.channel) filtered = filtered.filter((msg) => msg.channel === options.channel)
} }
if (options.since) { if (options.since) {
filtered = filtered.filter(msg => msg.timestamp >= options.since!) filtered = filtered.filter((msg) => msg.timestamp >= options.since!)
} }
if (options.limit) { if (options.limit) {
@@ -308,7 +308,7 @@ export class EventCommunicationService {
* 获取应用的订阅列表 * 获取应用的订阅列表
*/ */
getAppSubscriptions(appId: string): EventSubscriber[] { getAppSubscriptions(appId: string): EventSubscriber[] {
return Array.from(this.subscribers.values()).filter(sub => sub.appId === appId) return Array.from(this.subscribers.values()).filter((sub) => sub.appId === appId)
} }
/** /**
@@ -316,24 +316,21 @@ export class EventCommunicationService {
*/ */
getChannelSubscriberCount(channel: string): number { getChannelSubscriberCount(channel: string): number {
return Array.from(this.subscribers.values()).filter( return Array.from(this.subscribers.values()).filter(
sub => sub.channel === channel && sub.active (sub) => sub.channel === channel && sub.active,
).length ).length
} }
/** /**
* 创建通信频道 * 创建通信频道
*/ */
createChannel( createChannel(channel: string, config: Omit<CommunicationChannel, 'name'>): boolean {
channel: string,
config: Omit<CommunicationChannel, 'name'>
): boolean {
if (this.channels.has(channel)) { if (this.channels.has(channel)) {
return false return false
} }
this.channels.set(channel, { this.channels.set(channel, {
name: channel, name: channel,
...config ...config,
}) })
console.log(`创建通信频道: ${channel}`) console.log(`创建通信频道: ${channel}`)
@@ -349,7 +346,7 @@ export class EventCommunicationService {
.filter(([, sub]) => sub.channel === channel) .filter(([, sub]) => sub.channel === channel)
.map(([id]) => id) .map(([id]) => id)
subscribersToRemove.forEach(id => this.unsubscribe(id)) subscribersToRemove.forEach((id) => this.unsubscribe(id))
// 删除频道 // 删除频道
const result = this.channels.delete(channel) const result = this.channels.delete(channel)
@@ -376,9 +373,7 @@ export class EventCommunicationService {
// 清理过期消息 // 清理过期消息
for (const [appId, messages] of this.messageQueue.entries()) { for (const [appId, messages] of this.messageQueue.entries()) {
const validMessages = messages.filter(msg => const validMessages = messages.filter((msg) => !msg.expiresAt || msg.expiresAt > now)
!msg.expiresAt || msg.expiresAt > now
)
if (validMessages.length !== messages.length) { if (validMessages.length !== messages.length) {
this.messageQueue.set(appId, validMessages) this.messageQueue.set(appId, validMessages)
@@ -388,7 +383,7 @@ export class EventCommunicationService {
// 清理消息历史(保留最近7天) // 清理消息历史(保留最近7天)
const sevenDaysAgo = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000) const sevenDaysAgo = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000)
for (const [appId, history] of this.messageHistory.entries()) { for (const [appId, history] of this.messageHistory.entries()) {
const recentHistory = history.filter(msg => msg.timestamp > sevenDaysAgo) const recentHistory = history.filter((msg) => msg.timestamp > sevenDaysAgo)
this.messageHistory.set(appId, recentHistory) this.messageHistory.set(appId, recentHistory)
} }
@@ -424,7 +419,7 @@ export class EventCommunicationService {
restricted: true, restricted: true,
allowedApps: ['system'], allowedApps: ['system'],
maxMessageSize: 1024 * 10, // 10KB maxMessageSize: 1024 * 10, // 10KB
messageRetention: 24 * 60 * 60 * 1000 // 24小时 messageRetention: 24 * 60 * 60 * 1000, // 24小时
}) })
// 应用间通信频道 // 应用间通信频道
@@ -433,7 +428,7 @@ export class EventCommunicationService {
restricted: false, restricted: false,
allowedApps: [], allowedApps: [],
maxMessageSize: 1024 * 100, // 100KB maxMessageSize: 1024 * 100, // 100KB
messageRetention: 7 * 24 * 60 * 60 * 1000 // 7天 messageRetention: 7 * 24 * 60 * 60 * 1000, // 7天
}) })
// 用户交互频道 // 用户交互频道
@@ -442,7 +437,7 @@ export class EventCommunicationService {
restricted: false, restricted: false,
allowedApps: [], allowedApps: [],
maxMessageSize: 1024 * 5, // 5KB maxMessageSize: 1024 * 5, // 5KB
messageRetention: 60 * 60 * 1000 // 1小时 messageRetention: 60 * 60 * 1000, // 1小时
}) })
// 广播频道 // 广播频道
@@ -451,7 +446,7 @@ export class EventCommunicationService {
restricted: true, restricted: true,
allowedApps: ['system'], allowedApps: ['system'],
maxMessageSize: 1024 * 50, // 50KB maxMessageSize: 1024 * 50, // 50KB
messageRetention: 24 * 60 * 60 * 1000 // 24小时 messageRetention: 24 * 60 * 60 * 1000, // 24小时
}) })
} }
@@ -491,7 +486,7 @@ export class EventCommunicationService {
const queue = this.messageQueue.get(queueKey)! const queue = this.messageQueue.get(queueKey)!
// 按优先级插入 // 按优先级插入
const insertIndex = queue.findIndex(msg => msg.priority < message.priority) const insertIndex = queue.findIndex((msg) => msg.priority < message.priority)
if (insertIndex === -1) { if (insertIndex === -1) {
queue.push(message) queue.push(message)
} else { } else {
@@ -510,7 +505,9 @@ export class EventCommunicationService {
message.status = MessageStatus.FAILED message.status = MessageStatus.FAILED
// 只对非系统频道显示警告信息 // 只对非系统频道显示警告信息
if (message.channel !== 'system') { if (message.channel !== 'system') {
console.warn(`[EventCommunication] 没有找到频道 ${message.channel} 的订阅者[消息 ID: ${message.id}]`) console.warn(
`[EventCommunication] 没有找到频道 ${message.channel} 的订阅者[消息 ID: ${message.id}]`,
)
} }
return return
} }
@@ -525,7 +522,9 @@ export class EventCommunicationService {
await subscriber.handler(message) await subscriber.handler(message)
this.statistics.totalMessagesReceived++ this.statistics.totalMessagesReceived++
console.log(`[EventCommunication] 消息 ${message.id} 已投递给订阅者 ${subscriber.id}[频道: ${message.channel}]`) console.log(
`[EventCommunication] 消息 ${message.id} 已投递给订阅者 ${subscriber.id}[频道: ${message.channel}]`,
)
} catch (error) { } catch (error) {
console.error(`向订阅者 ${subscriber.id} 发送消息失败:`, error) console.error(`向订阅者 ${subscriber.id} 发送消息失败:`, error)
throw error throw error
@@ -534,7 +533,6 @@ export class EventCommunicationService {
await Promise.allSettled(deliveryPromises) await Promise.allSettled(deliveryPromises)
message.status = MessageStatus.DELIVERED message.status = MessageStatus.DELIVERED
} catch (error) { } catch (error) {
message.status = MessageStatus.FAILED message.status = MessageStatus.FAILED
this.statistics.failedMessages++ this.statistics.failedMessages++
@@ -553,7 +551,7 @@ export class EventCommunicationService {
* 获取相关订阅者 * 获取相关订阅者
*/ */
private getRelevantSubscribers(message: EventMessage): EventSubscriber[] { private getRelevantSubscribers(message: EventMessage): EventSubscriber[] {
return Array.from(this.subscribers.values()).filter(subscriber => { return Array.from(this.subscribers.values()).filter((subscriber) => {
if (!subscriber.active) return false if (!subscriber.active) return false
if (subscriber.channel !== message.channel) return false if (subscriber.channel !== message.channel) return false
@@ -603,9 +601,7 @@ export class EventCommunicationService {
const now = new Date() const now = new Date()
for (const [queueKey, messages] of this.messageQueue.entries()) { for (const [queueKey, messages] of this.messageQueue.entries()) {
const validMessages = messages.filter(msg => const validMessages = messages.filter((msg) => !msg.expiresAt || msg.expiresAt > now)
!msg.expiresAt || msg.expiresAt > now
)
if (validMessages.length !== messages.length) { if (validMessages.length !== messages.length) {
this.messageQueue.set(queueKey, validMessages) this.messageQueue.set(queueKey, validMessages)
@@ -636,7 +632,8 @@ export class EventCommunicationService {
* 更新活跃订阅者数量 * 更新活跃订阅者数量
*/ */
private updateActiveSubscribersCount(): void { private updateActiveSubscribersCount(): void {
this.statistics.activeSubscribers = Array.from(this.subscribers.values()) this.statistics.activeSubscribers = Array.from(this.subscribers.values()).filter(
.filter(sub => sub.active).length (sub) => sub.active,
).length
} }
} }

View File

@@ -1,4 +1,4 @@
import { ref, reactive } from 'vue' import { reactive, ref } from 'vue'
import type { IEventBuilder, IEventMap } from '@/events/IEventBuilder' import type { IEventBuilder, IEventMap } from '@/events/IEventBuilder'
import { v4 as uuidv4 } from 'uuid' import { v4 as uuidv4 } from 'uuid'
import type { ResizeDirection, ResizeState } from '@/ui/types/WindowFormTypes' import type { ResizeDirection, ResizeState } from '@/ui/types/WindowFormTypes'

View File

@@ -35,7 +35,7 @@ const builtInWindows = ref<BuiltInWindow[]>([])
const systemService = inject<SystemServiceIntegration>('systemService') const systemService = inject<SystemServiceIntegration>('systemService')
// 添加内置应用窗口 // 添加内置应用窗口
const addBuiltInWindow = (windowId: string, appId: string) => { const addBuiltInWindow = async (windowId: string, appId: string) => {
// 检查应用是否存在 // 检查应用是否存在
const appRegistration = appRegistry.getApp(appId) const appRegistration = appRegistry.getApp(appId)
if (!appRegistration) { if (!appRegistration) {
@@ -50,11 +50,23 @@ const addBuiltInWindow = (windowId: string, appId: string) => {
return return
} }
// 处理异步组件加载
let component = appRegistration.component
if (typeof component === 'function') {
try {
// 如果是函数,调用它来获取组件
component = await component()
} catch (error) {
console.error(`加载应用组件失败: ${appId}`, error)
return
}
}
// 添加窗口 // 添加窗口
const window: BuiltInWindow = { const window: BuiltInWindow = {
id: windowId, id: windowId,
appId, appId,
component: appRegistration.component, component,
props: { props: {
windowId, windowId,
appId appId
@@ -99,9 +111,9 @@ onMounted(() => {
const eventService = systemService.getEventService() const eventService = systemService.getEventService()
// 监听内置应用窗口创建事件 // 监听内置应用窗口创建事件
const subscriberId = eventService.subscribe('system', 'built-in-window-created', (message) => { const subscriberId = eventService.subscribe('system', 'built-in-window-created', async (message) => {
const { windowId, appId } = message.payload const { windowId, appId } = message.payload
addBuiltInWindow(windowId, appId) await addBuiltInWindow(windowId, appId)
}) })
// 监听内置应用窗口关闭事件 // 监听内置应用窗口关闭事件

View File

@@ -144,7 +144,7 @@ const startApp = async (appId: string) => {
// 使用主应用的窗口管理器来渲染内置应用 // 使用主应用的窗口管理器来渲染内置应用
if (windowManager?.value) { if (windowManager?.value) {
windowManager.value.addBuiltInWindow(windowInstance.id, appId) await windowManager.value.addBuiltInWindow(windowInstance.id, appId)
console.log(`[主应用] 使用窗口管理器渲染内置应用: ${appId}`) console.log(`[主应用] 使用窗口管理器渲染内置应用: ${appId}`)
} else { } else {
console.error('窗口管理器未初始化') console.error('窗口管理器未初始化')

View File

@@ -26,5 +26,26 @@ export default defineConfig({
'@': fileURLToPath(new URL('./src', import.meta.url)), '@': fileURLToPath(new URL('./src', import.meta.url)),
'vue': 'vue/dist/vue.esm-bundler.js' 'vue': 'vue/dist/vue.esm-bundler.js'
} }
},
build: {
rollupOptions: {
output: {
// 配置代码分割
manualChunks: {
// 将Vue相关库打包到单独的chunk中
vue: ['vue', 'vue-router', 'pinia'],
// 将UI库打包到单独的chunk中
ui: ['naive-ui'],
// 将工具库打包到单独的chunk中
utils: ['lodash', 'uuid'],
// 将VueUse打包到单独的chunk中
vueuse: ['@vueuse/core']
}
}
},
// 启用压缩
minify: 'esbuild',
// 启用CSS代码分割
cssCodeSplit: true
} }
}) })