做 HarmonyOS 应用时最容易低估的不是某个页面怎么写而是页面、数据、资源、扩展能力之间的边界怎么放。这个项目是一个“桌面卡片工具”用户可以浏览模板、创建卡片、收藏、归档、备份并把指定卡片同步到 HarmonyOS 桌面卡片。这篇先讲整体架构后面的文章再拆本地持久化、分类模板、Form Kit、备份恢复和统计页面。项目结构先按能力分层工程的主模块是entry页面、组件、服务和公共模型都放在entry/src/main/ets下entry/src/main/ets ├── common │ ├── AppModels.ets │ ├── AppSizes.ets │ ├── AppTheme.ets │ ├── CardImages.ets │ ├── DesignData.ets │ └── Routes.ets ├── components │ ├── BottomNavBar.ets │ ├── ShowcaseCard.ets │ ├── MenuListItem.ets │ └── PageHeader.ets ├── pages │ ├── Index.ets │ ├── CategoryPage.ets │ ├── CardDetailPage.ets │ ├── CardEditPage.ets │ ├── CardManagePage.ets │ ├── StatisticsPage.ets │ └── BackupPage.ets ├── services │ ├── AppDataService.ets │ └── DesktopFormService.ets ├── entryability ├── entryformability └── entrybackupability这个拆法的重点是页面只负责展示和轻量交互真正的数据读写放在AppDataService.ets。这样首页、分类页、详情页、管理页和桌面 Form 都能拿到同一份状态避免每个页面自己维护一套临时数据。页面注册不要让路由散落在页面里页面列表在main_pages.json里集中声明{ src: [ pages/Index, pages/MarketPage, pages/CategoryPage, pages/CardDetailPage, pages/CardEditPage, pages/CardStylePage, pages/StatisticsPage, pages/ReminderPage, pages/ThemeStorePage, pages/CardManagePage, pages/BackupPage, pages/ProfilePage, pages/AboutPage ] }应用内跳转再通过RoutePaths统一管理。这样有两个好处页面路径变化时只需要更新常量层。共享组件和服务模型只保存稳定 route不硬编码字符串。在 ArkTS 严格模式下裸对象和动态 key 访问都容易引发类型问题。把路由集中成静态类也能减少编译器误判。Ability 入口先初始化数据再加载主页面应用入口是EntryAbility.ets。这里做了两件事初始化数据服务加载pages/Index。onWindowStageCreate(windowStage: window.WindowStage): void { this.windowStage windowStage; appDataService.initialize(this.context); windowStage.loadContent(pages/Index, (err) { if (err.code) { return; } this.registerIconFontIfNeeded(); }); }这段代码看起来简单但顺序很关键先initialize()页面渲染时才能读到持久化状态。loadContent()成功后再注册 iconfont规避窗口状态未就绪导致的图标注册异常。onForeground()中也补一次registerIconFontIfNeeded()保证恢复前台后图标仍然稳定。项目以前遇到过window state abnormal一类问题所以没有把 iconfont 注册写在更早的生命周期里。主入口不是单页堆叠而是“首页 分类 我的”的本地 TabIndex.ets里没有通过 router 切三个一级页而是用currentTab控制首页、分类、个人中心三个主视图State currentTab: string home; State categoryQuery: string ; State selectedCategoryId: CardCategoryId countdown; private scroller: Scroller new Scroller();底部导航切换时重置搜索和分类状态再滚动回顶部private handleBottomNavChange(id: string): void { if (id create) { router.pushUrl({ url: RoutePaths.cardEdit }); return; } this.currentTab id; this.categoryQuery ; this.hasSelectedCategory false; this.scrollToTop(); }这种设计适合轻量工具类应用一级入口切换速度快状态刷新集中底部导航也不用反复创建页面栈。数据服务是项目的核心AppDataService.ets是整个项目最重要的文件。它负责从preferences读取和写入本地状态。维护模板目录、主题目录、样式目录。生成首页摘要、分类卡片、详情视图、统计数据。支持卡片创建、编辑、收藏、归档、恢复、删除。处理本地备份、系统备份和桌面 Form 数据。页面拿到的不是原始状态而是视图模型const detailView appDataService.getCardDetailView(params.cardId, params.templateId); this.card detailView.card; this.isTemplate detailView.isTemplate;这让页面不需要知道“模板卡片”和“用户卡片”的底层差异只根据isTemplate决定按钮文案和动作。扩展能力Form 和 Backup 都复用同一服务层项目里除了主应用还有两个扩展能力extensionAbilities: [ { name: EntryBackupAbility, type: backup }, { name: EntryFormAbility, type: form } ]EntryFormAbility通过DesktopFormService读取当前桌面卡片数据EntryBackupAbility通过AppDataService导出和恢复快照。这样主应用、桌面卡片、系统备份三条链路不会各自维护状态。这套架构适合什么项目如果你的 HarmonyOS 应用符合下面几个特征可以参考这个拆法页面多但核心状态只有一份。有本地编辑、收藏、归档、恢复一类操作。需要桌面卡片或备份扩展能力读取同一份业务数据。UI 需要大量复用卡片、列表、导航、搜索栏等组件。ArkTS 严格模式下希望减少动态对象和松散字段。验证建议架构完成后不要只看首页。建议按这个顺序跑一遍D:\dev\command-line-tools\bin\hvigorw.bat assembleHap --no-daemon --stacktrace然后手工检查启动首页确认AppDataService.initialize()后首屏能显示。切换首页、分类、我的确认底部导航状态不串。从分类进入详情再进入编辑确认路由参数不丢。新建卡片后回到管理页确认列表刷新。设置桌面卡片后确认 Form 数据能同步。小结这个项目的架构核心不是“页面很多”而是“状态只有一份能力围着状态走”。页面负责展示服务层负责业务状态资源层负责图片和主题Ability/ExtensionAbility 只做入口和桥接。对 HarmonyOS ArkTS 工程来说这种边界比一开始写出漂亮页面更重要。边界清楚之后后续加模板、加桌面卡片、加统计和备份都不会把页面逻辑越写越散。