【鸿蒙原生应用开发实战】第一篇:从零搭建"萌宠日记"项目——Stage模型与工程架构解析
哈喽大家好,我是AtomCode。最近在搞一个鸿蒙原生应用"萌宠日记",一个宠物健康管理+日常记录的小工具。本文是系列第一篇,带你从新建项目开始,一步步理解鸿蒙Stage模型的工程架构。
一、项目背景与功能概览
"萌宠日记"是一个面向养宠人群的轻量级管理工具,核心功能包括:
| 模块 | 功能说明 | 页面 |
|---|---|---|
| 首页概览 | 宠物卡片切换、快捷入口、动态信息流 | Index.ets |
| 添加宠物 | 表单录入宠物信息(类型/名字/品种/性别等) | AddPetPage.ets |
| 宠物详情 | 健康档案、体重趋势图表、疫苗接种记录 | PetDetailPage.ets |
| 萌宠相册 | 网格/列表双模式切换、按宠物筛选照片 | AlbumPage.ets |
| 提醒管理 | 疫苗/驱虫/美容提醒、今日待办、已完成归档 | ReminderPage.ets |
技术栈:ArkTS + Stage模型 + API 23 (HarmonyOS 6.1)
二、项目初始化与DevEco Studio配置
2.1 创建项目
打开DevEco Studio,选择File → New → Create Project:
- Application Name: 萌宠日记
- Bundle Name:
com.example.petdiary - Project Type: Application
- Compile SDK: API 23 (HarmonyOS 6.1+)
- Model: Stage
- Language: ArkTS
注意:API 23 对应的是 HarmonyOS 6.1,用的是 Stage 模型而非 FA 模型。Stage 模型是鸿蒙主推的元程序框架,和 FA 模型最大的区别是:Ability 和 UI 分离,支持多实例、组件化、更好的生命周期管理。
2.2 工程目录结构
创建完成后,项目结构如下:
MyApplication/ ├── AppScope/ # 全局应用配置 │ ├── app.json5 # 应用级配置(bundleName, version等) │ └── resources/ │ └── base/element/string.json # 全局字符串(app_name) ├── entry/ # 应用模块 │ ├── src/main/ │ │ ├── ets/ │ │ │ ├── entryability/ # Ability入口 │ │ │ └── pages/ # 页面目录(业务代码) │ │ ├── module.json5 # 模块配置 │ │ └── resources/ # 资源文件(颜色/字号/字符串) │ ├── build-profile.json5 # 模块级构建配置 │ └── oh-package.json5 # OHPM依赖管理 ├── build-profile.json5 # 项目级构建配置 ├── hvigor/ # 构建工具配置 └── oh_modules/ # 依赖包关键文件解读
AppScope/app.json5—— 应用的身份证:
{"app":{"bundleName":"com.example.myapplication","vendor":"example","versionCode":1000000,"versionName":"1.0.0","icon":"$media:layered_image","label":"$string:app_name"}}$string:app_name引用的是AppScope/resources/base/element/string.json中定义的app_name:
{"name":"app_name","value":"萌宠日记"}⚠️ 踩坑:
app_name只能在 AppScope 中定义一次。如果在entry模块里也定义同名的app_name,编译时会报冲突。
build-profile.json5—— SDK版本控制:
{"products":[{"name":"default","targetSdkVersion":"6.1.1(24)","compatibleSdkVersion":"6.1.0(23)","runtimeOS":"HarmonyOS"}]}compatibleSdkVersion: 23表示最低兼容 API 23,targetSdkVersion: 24表示目标 SDK 版本。
三、Stage模型的核心概念
3.1 Ability与UI分离
Stage 模型下,Ability是应用入口,负责生命周期管理;UI由页面组件独立完成。
看我们的EntryAbility.ets:
import{UIAbility,AbilityConstant,Want}from'@kit.AbilityKit';import{window}from'@kit.ArkUI';exportdefaultclassEntryAbilityextendsUIAbility{onCreate(want:Want,launchParam:AbilityConstant.LaunchParam):void{// Ability创建时的初始化逻辑this.context.getApplicationContext().setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_NOT_SET);}onWindowStageCreate(windowStage:window.WindowStage):void{// 加载首页windowStage.loadContent('pages/Index',(err)=>{if(err.code){hilog.error(0x0000,'testTag','Failed to load content: %{public}s',JSON.stringify(err));}});}}生命周期顺序:onCreate → onWindowStageCreate → onForeground。
注意:
@kit.AbilityKit是 API 23+ 的模块导入写法。如果你在 API 23 以下,需要用@ohos.ability.abilityLifecycleCallback。
3.2 页面路由注册
所有页面必须在main_pages.json中注册,否则跳转时会找不到页面:
{"src":["pages/Index","pages/AddPetPage","pages/PetDetailPage","pages/AlbumPage","pages/ReminderPage"]}module.json5中通过"pages": "$profile:main_pages"引用该配置文件。
3.3 路由跳转与参数传递
我们使用@ohos.router进行页面导航:
importrouterfrom'@ohos.router';// 跳转到详情页,携带参数router.pushUrl({url:'pages/PetDetailPage',params:{petId:1}});// 接收参数constparams:Record<string,Object>=router.getParams()asRecord<string,Object>;constpetId:number=params['petId']asnumber;// 返回上一页router.back();关于@ohos.routervs@kit.AbilityKit
这里有个重要的兼容性问题需要说明:
在 API 23 下,router模块从@ohos.router导入。有些同学在查阅官方文档时可能会看到@kit.AbilityKit的写法,但 API 23 的@kit.AbilityKit并不导出router。所以一定要用:
importrouterfrom'@ohos.router';// ✅ 正确// import router from '@kit.AbilityKit'; // ❌ API 23 不适用四、资源管理体系
鸿蒙的资源管理通过$r和$media等语法糖实现。我们项目中用到了三类资源:
4.1 颜色资源color.json
{"color":[{"name":"start_window_background","value":"#FFFFFF"},{"name":"primary_color","value":"#FF6B35"},{"name":"background_color","value":"#F5F5F5"},{"name":"header_bg","value":"#1A1A2E"}]}使用方式:$r('app.color.primary_color')或直接在代码中用字符串'#FF6B35'。
4.2 字号资源float.json
{"float":[{"name":"title_font_size","value":"22fp"},{"name":"subtitle_font_size","value":"16fp"},{"name":"card_radius","value":"12vp"},{"name":"list_item_height","value":"80vp"}]}
fp是字体像素(Font Pixel),会跟随系统字体大小缩放;vp是虚拟像素(Virtual Pixel),适配不同屏幕密度。
4.3 为什么不把所有样式写在资源文件里?
很多人刚接触鸿蒙开发会问:样式写在代码里还是资源文件里?
我的实践原则是:
- 全局统一Token→ 资源文件(主题色、主字体、圆角标准)
- 页面局部样式→ 代码内直接写(方便调试、所见即所得)
我们的项目里颜色直接在代码中写字符串,是因为每个页面有自己的配色风格,抽到资源文件反而增加维护成本。
五、构建与运行
5.1 命令行构建
DevEco Studio 提供了图形化构建,但我们也可以命令行构建:
node"D:\DevEco Studio\tools\node\node.exe""D:\DevEco Studio\tools\hvigor\bin\hvigorw.js"\--modemodule\-pmodule=entry@default\-pproduct=default\-prequiredDeviceType=phone\assembleHap\--analyze=normal\--parallel\--incremental\--daemon参数说明:
--mode module:模块级构建-p module=entry@default:构建 entry 模块的 default 产品assembleHap:打包成 HAP 文件--incremental:增量编译,二次构建更快--daemon:守护进程模式
5.2 真机调试
鸿蒙应用调试推荐使用远程模拟器或真机:
- 在 DevEco Studio 中登录华为账号
- 连接设备或启动模拟器
- 点击运行按钮(▶️)
六、踩坑总结
这一篇先列几个新手必踩的坑:
| 问题 | 表现 | 解决方案 |
|---|---|---|
app_name重复定义 | 编译报 resource conflict | 只在 AppScope 中定义一次 |
router导入路径错误 | 编译报找不到模块 | 用@ohos.router而非@kit.AbilityKit |
| 页面跳转报 404 | router.pushUrl找不到页面 | 检查main_pages.json中是否注册 |
| 对象字面量类型错误 | arkts-no-untyped-obj-literals | 为对象字面量显式声明类型/提取为类型变量 |
七、下篇预告
下一篇我们开始写代码!重点分析首页(Index.ets)的实现:
- 宠物卡片横向滚动选择器
- 快速操作功能区(4个核心入口)
- 萌宠动态信息流(ForEach 渲染、Emoji 搭配)
咱们下篇见!🚀
SDK版本: API 23 (HarmonyOS 6.1) |框架: Stage模型 + ArkTS