HarmonyOS实战《疆域纪行》第01篇|项目跑起来:DevEco 导入、SDK 配置与安全脱敏
这一篇先不急着写复杂页面,而是把项目从“能打开工程”到“能理解启动链路”跑通。重点看三个问题:工程模块怎么分、Stage 模型从哪里加载首页、发布配置里哪些内容可以讲、哪些内容必须脱敏。
项目落点:
- 入口 Ability:
entry/src/main/ets/entryability/EntryAbility.ets - 主页面:
entry/src/main/ets/pages/Index.ets - 模块配置:
entry/src/main/module.json5 - 构建配置:
build-profile.json5、entry/build-profile.json5
开篇:为什么选择“离线旅行攻略”这个题目
很多 HarmonyOS Demo 会从待办、计数器、天气页开始,优点是简单,缺点是离真实 App 有距离。《疆域纪行》选择的是一个更接近上架产品的场景:新疆旅行攻略。
这个场景天然包含几个很适合练手的技术点:
- 内容多:景点、美食、城市、路线、贴士都要结构化管理。
- 页面多:首页、探索页、路线页、收藏页、我的页、详情页都要完整串起来。
- 强视觉:旅行 App 需要大图、卡片、标签、沉浸式详情页。
- 离线可用:不能依赖后端,所有核心内容要打包进应用。
- 本地状态:收藏、搜索历史、行程清单要在重启后保留。
最终这个项目不是“能跑就行”的页面拼接,而是一套完整的 local-first App 骨架。它特别适合作为 HarmonyOS 初中级开发者的实战项目。
工程结构先看清楚
项目核心文件集中在 entry 模块:
entry/src/main/ets/
├── data/
│ └── AtlasData.ets
├── entryability/
│ └── EntryAbility.ets
├── models/
│ └── AtlasModels.ets
├── pages/
│ └── Index.ets
└── services/
└── LocalAppStore.ets
这套结构的好处是边界非常清晰:
models放类型定义,先把业务对象说清楚。data放离线内容库,运行时不需要接口。services放本地存储读写,页面不直接碰持久化细节。pages负责页面状态、布局、交互和页面切换。entryability负责启动 Ability 并加载主页面。
这是做 HarmonyOS 单机应用时很推荐的一种轻量分层。项目还没有复杂到需要一堆模块和路由框架,但也没有把所有逻辑散落在 UI 里。
Stage 模型入口:EntryAbility 做什么
入口 Ability 的核心代码如下:
export default class EntryAbility extends UIAbility {
onWindowStageCreate(windowStage: window.WindowStage): void {
windowStage.loadContent('pages/Index', (err) => {
if (err.code) {
hilog.error(DOMAIN, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err));
return;
}
hilog.info(DOMAIN, 'testTag', 'Succeeded in loading the content.');
});
}
}
这里没有做复杂路由,而是直接加载 pages/Index。原因很实际:这个 App 的页面层级主要靠状态驱动,底部 Tab、详情页、搜索页都可以在一个主页面里完成。
这种做法适合 MVP:
- 启动路径短。
- 状态集中,调试成本低。
- 页面跳转不用维护很多路由常量。
- 单机内容类 App 的交互闭环更快。
后续如果要拆成多页面,可以再把详情页、搜索页、路线编辑页独立出去。MVP 阶段先做“可运行、可浏览、可收藏”,是更划算的选择。
module.json5:设备与入口配置
entry/src/main/module.json5 中声明了设备类型:
"deviceTypes": [
"phone",
"tablet",
"2in1"
]
这说明项目一开始就不是只面向手机竖屏,而是预留了平板和 2in1 设备。旅行攻略类 App 在大屏上的体验很有价值:图片更大、路线信息更清楚、详情页阅读更舒服。
Ability 配置中还设置了启动图标和启动背景:
"startWindowIcon": "$media:startIcon",
"startWindowBackground": "$color:start_window_background"
启动体验不是锦上添花。对于一个图片很多的 App,启动时如果有统一的背景色和图标,可以避免冷启动阶段出现突兀白屏。
页面架构:一个 Index 承担主流程
Index.ets 是整个 App 的核心。它的状态定义很直观:
@State activeTab: string = 'home';
@State detailId: string = '';
@State detailKind: string = '';
@State searchText: string = '';
@State exploreType: string = 'spot';
@State exploreRegion: string = 'all';
@State exploreTheme: string = 'all';
@State exploreSort: string = 'recommend';
@State favoriteType: string = 'all';
@State favoriteIds: string[] = [];
@State searchHistory: string[] = [];
@State itineraryIds: string[] = [];
@State showLaunchSplash: boolean = true;
这组状态可以分成四类:
- 页面导航:
activeTab、detailId、detailKind - 搜索筛选:
searchText、exploreType、exploreRegion、exploreTheme、exploreSort - 本地数据:
favoriteIds、searchHistory、itineraryIds - 启动体验:
showLaunchSplash
这就是一个单机 App 最小但完整的状态模型。页面显示什么,不靠隐式路由猜,而是由状态直接决定。
build 方法:状态驱动页面壳
主页面的 build() 非常关键:
build() {
Stack({ alignContent: Alignment.TopStart }) {
if (this.showLaunchSplash) {
this.LaunchSplash()
} else {
Column() {
if (this.detailId.length > 0 && this.detailKind === 'route') {
this.RouteDetail(this.findRouteItem(this.detailId));
} else if (this.detailId.length > 0) {
this.AtlasDetail(this.findAtlasItem(this.detailId));
} else {
this.MainShell();
}
}
.width('100%')
.height('100%')
.backgroundColor('#F6F1E8')
}
}
.width('100%')
.height('100%')
}
这段代码的亮点是把主流程拆成三层:
- 启动页:
showLaunchSplash - 详情页:
detailId和detailKind - 主 Shell:底部导航和各 Tab 页面
这种结构很适合内容型 App。详情页不一定要真的走系统路由,只要让 detailId 成为页面状态,就能做到“从任何卡片进入详情,从返回键回到原页面”。
返回键处理:让详情页像自然导航
项目里还实现了 onBackPress():
onBackPress(): boolean {
if (this.detailId.length > 0) {
this.closeDetail();
return true;
}
return false;
}
当用户处在详情页时,系统返回键会清空详情状态,而不是直接退出 App。这个细节很小,但很影响真实使用体验。
对内容类 App 来说,用户经常从首页、探索页、收藏页反复进入详情。如果返回逻辑不自然,会显得非常粗糙。
为什么 MVP 阶段不急着上后端
《疆域纪行》的第一版定位是完全离线:
- 景点、美食、路线数据内置在
AtlasData.ets - 图片放在
resources/base/media - 收藏、历史、行程存入本机 Preferences
- 无需登录
- 无需网络
这不是“偷懒”,反而是一种产品策略。新疆旅行场景里,弱网和无网并不少见。攻略信息如果能离线查看,核心价值会更稳。
当然,离线 App 也有边界:门票、开放时间、交通政策、餐厅营业状态可能变化。因此项目在“我的”页面和详情页都加入了免责声明,把动态信息弱化为“旅行参考”。
本篇小结
这篇先完成了整体工程认知:
- Stage 模型通过
EntryAbility加载pages/Index - 页面主流程由
@State驱动 - 首页、探索、路线、收藏、我的都放在统一 Shell 下
- 详情页通过
detailId/detailKind切换 - 离线内容库和本地状态形成第一版核心闭环
下一篇我们继续看这个 App 最核心的底座:如何用 ArkTS 类型和静态数据,搭建一个可扩展的离线内容库。
更多推荐

所有评论(0)