Files
AtomUI/docs/Gallery/06-RisksAndTechDebt.md

7.9 KiB
Raw Permalink Blame History

AtomUI Gallery — 风险点、技术债与迭代注意事项

文档版本:2026-04-15


1. 技术债与已知问题

1.1 ⚠️ 文件命名不规范

以下文件使用了非标准扩展名,可能导致 IDE 关联错误或构建问题:

文件 问题 建议
FormShowCase.axmal.cs .axmal 拼写错误(应为 .axaml) 重命名为 FormShowCase.axaml.cs
RateShowCase.axmal.cs 同上 重命名为 RateShowCase.axaml.cs
TourShowCase.axmal.cs 同上 重命名为 TourShowCase.axaml.cs
ResultShowCase.cs.axaml .cs.axaml 顺序反转(应为 .axaml 单独文件) 重命名为 ResultShowCase.axaml,代码后置改为 ResultShowCase.axaml.cs
CustomizeThemeShowCase.cs 代码后置直接 .cs(对应 .axaml 存在) 重命名为 CustomizeThemeShowCase.axaml.cs

风险:在某些 IDE 或工具链中,文件关联可能断裂,导致 XAML 设计器无法正常工作。

1.2 ⚠️ IActivatableViewModel 使用不一致

在 67 个 ShowCase ViewModel 中:

  • 仅 8 个实现了 IActivatableViewModel(AboutUs、Button、OsInfo、Avatar、Badge、AutoComplete、Cascader、CheckBox)
  • 其余 59 个未实现

然而,所有 View 都在构造函数中调用了 this.WhenActivated(disposables => { ... })。

影响:

  • 对于未实现 IActivatableViewModel 的 ViewModel,WhenActivated 仍然可以在 View 端正常工作(因为 ReactiveUserControl 自身支持激活)
  • 但如果未来需要在 ViewModel 端监听激活/反激活事件,需要逐一添加 IActivatableViewModel

建议:统一规范 — 要么所有 ViewModel 都实现 IActivatableViewModel,要么都不实现。

1.3 ⚠️ 双重注册机制

每新增一个 ShowCase 需要在 三个位置 手动注册:

  1. ShowCaseViewModule.RegisterViews() — VM → View 映射
  2. CaseNavigationViewModel.RegisterShowCaseViewModels() — VM 工厂
  3. CaseNavigation.axaml — 导航菜单项

加上本地化资源(en_US.cs + zh_CN.cs),总计 5 个文件 需要同步修改。

风险:遗漏任何一处都会导致运行时错误或功能缺失。

建议:未来可考虑使用源代码生成器或反射注册自动化此流程。但鉴于当前 AOT 兼容性要求,显式注册是正确选择。

1.4 ⚠️ Roots.xml 中的过时引用

AtomUIGallery.Desktop/Roots.xml 中包含过时的程序集名称:

<assembly fullname="AtomUI.Demo.Desktop" preserve="All"/>  <!-- 应为 AtomUIGallery.Desktop -->
<assembly fullname="AtomUI.IconPkg" preserve="All"/>       <!-- 应为 AtomUI.Icons.Shared 或已不存在 -->

建议:修正为正确的程序集名称。

1.5 ⚠️ LinuxDistributionDetector 代码量过大

LinuxDistributionDetector.cs 共 380 行,包含大量平台特定检测逻辑。该工具类仅被 OsInfo ShowCase 使用。

建议:考虑是否可以简化或使用第三方库替代。

1.6 ⚠️ CommunityToolkit.Mvvm 使用不充分

项目引用了 CommunityToolkit.Mvvm 包(8.4.2),但实际使用较少,核心 MVVM 依赖 ReactiveUI。

建议:

  • 如果不需要 CommunityToolkit.Mvvm 的功能,移除引用以减少依赖
  • 如果需要,明确其使用场景并统一规范

2. 架构风险点

2.1 反射使用(AOT 兼容性)

以下位置使用了反射,在 AOT/NativeAOT 环境下需要特别注意:

位置 反射用途 AOT 风险
IconGallery.ReLoadIcons() 通过 CachedLoadedAssemblyTypeScanner 扫描 AtomUI.Icons.AntDesign 程序集中所有 Icon 子类 ⚠️ 高 — 需要 Trimmer 保留
GalleryIconProvider.GetTypeForKind() Type.GetType() + Assembly.GetType() 反射加载图标类型 ⚠️ 高
EnumExtension.ProvideValue() Enum.GetValuesAsUnderlyingType() 低风险

缓解措施:Roots.xml 中已标记相关程序集 preserve="All",但需确保列表准确。

2.2 每次导航创建新 View 实例

ShowCaseViewModule 中的工厂每次都创建新的 View:

locator.Map<ButtonViewModel, ButtonShowCase>(() => new ButtonShowCase());

同时 CaseNavigationViewModel.NavigateTo() 每次都创建新的 ViewModel。

影响:

  • 内存:旧 View/ViewModel 依赖 GC 回收
  • 性能:复杂 ShowCase(如 DataGrid、IconGallery)每次切换都需重新初始化
  • 状态丢失:切换回已访问的 ShowCase 时所有状态重置

建议:对于性能敏感的 ShowCase,可考虑添加 ViewModel/View 缓存策略。

2.3 事件处理与 Dispose 不一致

部分 ShowCase View(如 ModalShowCase)在 WhenActivated 中正确使用 CompositeDisposable 管理事件订阅生命周期:

this.WhenActivated(disposables =>
{
    BasicOpenModalButton.Click += HandleBasicModalButtonClick;
    disposables.Add(Disposable.Create(() => BasicOpenModalButton.Click -= HandleBasicModalButtonClick));
});

但其他 ShowCase View 虽然调用了 WhenActivated,却只是空回调 disposables => { }。

风险:如果 View 中直接在构造函数外订阅事件且未取消,可能导致内存泄漏。


3. 构建与发布注意事项

3.1 Debug vs Release 依赖差异

  • Debug:使用 <ProjectReference> 直接引用本地 AtomUI 源码(便于调试)
  • Release:使用 <PackageReference> 引用 NuGet 包(发布独立可用)

修改 AtomUI 控件源码后,必须确保 Debug 和 Release 都能正常编译。

3.2 源代码生成器输出

  • GeneratedFiles/ 目录会自动由 Roslyn 生成器填充
  • <Compile Remove="$(CompilerGeneratedFilesOutputPath)/**/*.cs"/> 确保不会重复编译
  • 禁止手动编辑 GeneratedFiles/ 下的文件

3.3 安装包构建

AtomUIGallery.Desktop 包含多平台安装包配置:

  • macOS:DMG(InstallerConfig.dmg.xml)
  • Windows:WiX MSI(InstallerConfig.wix.xml)
  • Linux:AppImage(InstallerConfig.appimage.xml)

发布脚本:scripts/PublishToLocal.ps1


4. 性能注意事项

4.1 图标画廊加载

IconGallery 使用反射扫描 + FrozenSet 缓存,首次加载时会扫描整个 AtomUI.Icons.AntDesign 程序集。虽然有 5 秒缓存超时机制,但首次加载可能较慢。

4.2 自动遍历测试

F5 自动遍历以 300ms 间隔切换页面,由于每次创建新 View/ViewModel,长时间运行可能导致内存增长。这是预期行为(用于压力测试),但需关注 GC 表现。


5. 迭代开发检查清单

新增或修改 ShowCase 时,务必完成以下检查:

  • ViewModel 正确实现 IRoutableViewModel,ID 唯一
  • View 继承 ReactiveUserControl<TViewModel>,构造函数中调用 WhenActivated + InitializeComponent
  • ShowCaseViewModule.RegisterViews() 中注册 VM → View 映射
  • CaseNavigationViewModel 中注册 VM 工厂
  • CaseNavigation.axaml 中添加导航菜单项
  • en_US.cs 和 zh_CN.cs 中添加本地化字符串
  • 亮色主题下正常显示
  • 暗色主题下正常显示
  • 紧凑模式下正常显示
  • 中文/英文语言切换正常
  • F5 自动遍历不崩溃
  • ShowCaseItem 的 Title 和 Description 准确描述功能
  • 文件命名遵循 .axaml + .axaml.cs 标准(不要写错为 .axmal)

6. 未来改进建议

优先级 建议 说明
🔴 高 修复文件命名错误 修正 .axmal 拼写错误和 .cs.axaml 顺序反转
🔴 高 修正 Roots.xml 过时引用 确保 Trimmer 配置正确
🟡 中 统一 IActivatableViewModel 使用 要么全部实现,要么全部移除
🟡 中 考虑 ViewModel/View 缓存策略 避免重复创建复杂控件
🟢 低 添加 ShowCase 自动注册 减少手动维护成本(需平衡 AOT 兼容性)
🟢 低 简化 LinuxDistributionDetector 考虑使用第三方库或精简逻辑