HarmonyOS NEXT 新闻资讯应用分层模块化开发全指南:状态管理V2 + 分层架构实战
环境 :DevEco Studio 6.1 | HarmonyOS SDK 6.1.0(23) | ArkTS
技术亮点 :状态管理V2全家桶 · HdsTabs沉浸光感悬浮页签 + Navigation路由 · 分层模块化架构
适用人群:HarmonyOS中高级开发者,希望掌握最新API和架构最佳实践
效果
---
一、项目概述
1.1 项目背景
本案例参考华为官方新闻资讯应用架构设计指南,采用分层模块化 架构,结合HarmonyOS 6.1最新的状态管理V2装饰器,从零构建一个功能完整的新闻资讯应用。
1.2 功能模块
| 模块 | 功能点 | 模块类型 |
|---|---|---|
| 新闻 | 新闻列表、新闻详情、新闻分类、下拉刷新 | HAR |
| 视频 | 视频列表(Grid)、视频播放 | HAR |
| 直播 | 直播列表(关注/直播/热门三Tab) | HAR |
| 我的 | 用户登录、评论管理、收藏、退出 | HAR |
| 服务 | 党媒云、社区融等服务 | HAR |
1.3 技术选型
| 技术点 | 选型 | 说明 |
|---|---|---|
| 状态管理 | V2(@ComponentV2) | 替代V1的@Component,更精准的响应式更新 |
| 底部导航 | HdsTabs + BottomTabBarStyle | @kit.UIDesignKit 悬浮页签,沉浸光感效果 |
| 页面路由 | Navigation + NavPathStack | 替代旧版router,支持声明式路由 |
| 数据懒加载 | LazyForEach + IDataSource | 性能优化的列表渲染方案 |
| 数据模型 | @ObservedV2 + @Trace | 细粒度属性级观测 |
二、分层模块化架构设计
2.1 三层架构
┌─────────────────────────────────────────────┐
│ 产品定制层 (Product) │
│ phone (HAP Entry) │
│ 页面框架 · 导航 · 启动页 · 路由配置 │
├─────────────────────────────────────────────┤
│ 基础特性层 (Features) │
│ news · video · live · personal · service │
│ 各模块独立HAR,仅依赖common │
├─────────────────────────────────────────────┤
│ 公共能力层 (Common) │
│ common (HAR) │
│ 数据模型 · 工具类 · 常量 · 通用组件 │
└─────────────────────────────────────────────┘
2.2 模块依赖关系图
product/phone (HAP)
├── common (HAR)
├── features/news (HAR) ──→ common
├── features/video (HAR) ──→ common
├── features/live (HAR) ──→ common
├── features/personal (HAR) ──→ common
└── features/service (HAR) ──→ common
核心原则:
- features层各模块互不依赖,只依赖common
- phone模块聚合所有features,是唯一入口
- common模块无外部依赖,是最底层基础设施
2.3 oh-package.json5 依赖配置
product/phone/oh-package.json5:
json
{
"dependencies": {
"common": "file:../../common",
"news": "file:../../features/news",
"video": "file:../../features/video",
"live": "file:../../features/live",
"personal": "file:../../features/personal",
"service": "file:../../features/service"
}
}
features/*/oh-package.json5(每个feature模块相同):
json
{
"dependencies": {
"common": "file:../../common"
}
}
三、状态管理V2全面应用
3.1 V1 → V2 装饰器映射表
本项目全面使用状态管理V2,以下是V1到V2的完整映射:
| V1装饰器 | V2装饰器 | 用途 |
|---|---|---|
@Component |
@ComponentV2 |
组件声明 |
@State |
@Local |
组件本地状态 |
@Prop |
@Param |
父→子单向传递 |
@Link |
@Param + @Event |
父子双向绑定 |
@Provide |
@Provider |
跨层级状态提供 |
@Consume |
@Consumer |
跨层级状态消费 |
@Watch |
@Monitor |
属性变化监听 |
@Observed |
@ObservedV2 + @Trace |
类属性细粒度观测 |
| 计算getter | @Computed |
计算属性 |
aboutToAppear |
aboutToAppear |
生命周期(不变) |
3.2 数据模型设计(@ObservedV2 + @Trace)
typescript
// common/src/main/ets/model/NewsItem.ets
@ObservedV2
export class NewsItem {
@Trace newsId: string = '';
@Trace newsTitle: string = '';
@Trace newsContent: string = '';
@Trace newsTime: string = '';
@Trace newsImage: string = '';
@Trace category: string = '头条';
@Trace isGood: boolean = false;
@Trace isCollect: boolean = false;
constructor(id: string, title: string, content: string,
time: string, image: string, category: string = '头条') {
this.newsId = id;
this.newsTitle = title;
this.newsContent = content;
this.newsTime = time;
this.newsImage = image;
this.category = category;
}
}
关键点 :@Trace标记的每个属性都是独立的观测单元,修改isGood不会触发仅读取newsTitle的组件重渲染。
3.3 组件间数据流
MainPage (@Provider 'pageStack')
└── Navigation(pageStack)
├── HdsTabs (悬浮页签内容)
│ ├── NewsHome ── pushPathByName()
│ ├── VideoHome ── pushPathByName()
│ ├── LiveHome
│ └── PersonalHome ── pushPathByName()
│
└── navDestination(pageMap)
├── NewsDetail ── @Consumer('pageStack') → pop()
├── VideoPlayer ── @Consumer('pageStack') → pop()
├── LoginPage ── @Consumer('pageStack') → pop()
└── MyComments ── @Consumer('pageStack') → pop()
四、HdsTabs 沉浸光感悬浮页签
4.1 HdsTabs 组件用法
项目使用 HdsTabs(@kit.UIDesignKit)实现底部悬浮导航,配合全局类 BottomTabBarStyle 和 SymbolGlyphModifier 实现带图标的悬浮页签,通过 .labelStyle() 确保图标与文本颜色一致。
typescript
import { HdsTabs } from '@kit.UIDesignKit';
import { SymbolGlyphModifier } from '@kit.ArkUI';
// BottomTabBarStyle 是全局类,不需要导入
@Entry
@ComponentV2
struct MainPage {
@Local currentTabIndex: number = 0;
@Provider('pageStack') pageStack: NavPathStack = new NavPathStack();
private buildTabIcon(symbol: Resource, selected: boolean): SymbolGlyphModifier {
return new SymbolGlyphModifier(symbol)
.fontColor([selected ? $r('app.color.focus_color') : $r('app.color.placeholder_color')]);
}
private buildTabBar(symbol: Resource, label: string): BottomTabBarStyle {
return new BottomTabBarStyle({
normal: this.buildTabIcon(symbol, false),
selected: this.buildTabIcon(symbol, true)
}, label).labelStyle({
unselectedColor: $r('app.color.placeholder_color'),
selectedColor: $r('app.color.focus_color')
});
}
build() {
Navigation(this.pageStack) {
Column() {
HdsTabs({ index: this.currentTabIndex }) {
TabContent() { NewsHome() }.tabBar(this.buildTabBar($r('sys.symbol.house'), '首页'))
TabContent() { VideoHome() }.tabBar(this.buildTabBar($r('sys.symbol.video'), '视频'))
TabContent() { LiveHome() }.tabBar(this.buildTabBar($r('sys.symbol.video_badge_adiowaves'), '直播'))
TabContent() { PersonalHome() }.tabBar(this.buildTabBar($r('sys.symbol.person'), '我的'))
}
.barOverlap(true)
.barPosition(BarPosition.End)
.vertical(false)
.barFloatingStyle({ barBottomMargin: 16 })
.onChange((index: number) => {
this.currentTabIndex = index;
})
.layoutWeight(1)
}
}
.hideTitleBar(true)
.navDestination(this.pageMap)
.mode(NavigationMode.Stack)
}
}
4.2 关键配置说明
| 配置 | 说明 |
|---|---|
HdsTabs |
从 @kit.UIDesignKit 导入 |
SymbolGlyphModifier |
从 @kit.ArkUI 导入,用于渲染系统图标 |
BottomTabBarStyle |
全局类,不需要导入 |
buildTabIcon |
构建带颜色的 SymbolGlyphModifier 图标 |
buildTabBar |
封装图标 + 文本 + .labelStyle() 颜色配置 |
.barOverlap(true) |
页签栏悬浮覆盖在内容之上 |
.barFloatingStyle() |
设置悬浮样式(底部间距等) |
.labelStyle() |
设置文本选中/未选中颜色,与图标颜色保持一致 |
五、Navigation + NavPathStack 路由管理
5.1 路由架构
本项目使用单Navigation容器 + 统一navDestination映射的路由方案:
typescript
// 在MainPage中注册路由映射
@Builder
pageMap(name: string, param: Object) {
if (name === 'NewsDetail') {
NewsDetail()
} else if (name === 'NewsCategory') {
NewsCategory()
} else if (name === 'VideoPlayer') {
VideoPlayer()
} else if (name === 'LoginPage') {
LoginPage()
} else if (name === 'MyComments') {
MyComments()
}
}
5.2 页面跳转方式
typescript
// 列表页 → 详情页(传递数据)
this.pageStack.pushPathByName('NewsDetail', newsItem);
// 详情页 → 返回
this.pageStack.pop();
5.3 启动页特殊处理
启动页(Index.ets)作为@Entry页面,通过loadContent('pages/Index')加载,其跳转使用传统router:
typescript
aboutToAppear() {
setTimeout(() => {
this.getUIContext().getRouter().replaceUrl({ url: 'pages/MainPage' });
}, 1500);
}
这是因为pages/Index和pages/MainPage是同级的Entry页面,不在Navigation容器内。
六、模块间交互方式
6.1 导航交互
| 场景 | 方式 | 代码示例 |
|---|---|---|
| Tab切换 | Tabs.onChange | this.currentTabIndex = index |
| 列表→详情 | pushPathByName | pageStack.pushPathByName('NewsDetail', item) |
| 详情→返回 | pop | pageStack.pop() |
| 启动页→主页 | router.replaceUrl | getRouter().replaceUrl({url:'pages/MainPage'}) |
6.2 数据交互
| 数据流 | V2机制 | 示例 |
|---|---|---|
| 父→子单向 | @Param |
NewsListItem({ item: newsItem }) |
| 子→父回调 | @Event |
onCountChange: (v) => { this.count = v } |
| 跨层级共享 | @Provider/@Consumer |
pageStack 在MainPage提供,各子组件消费 |
| 全局状态 | AppStorage |
userAccount、newsData评论列表 |
七、项目构建步骤
7.1 环境准备
- 安装 DevEco Studio 6.1
- 配置 HarmonyOS SDK 6.1.0(23)
- 创建空项目,SDK选择
6.1.0(23)
7.2 模块创建
在 build-profile.json5 中注册所有模块:
json
{
"modules": [
{ "name": "phone", "srcPath": "./product/phone", "targets": [{ "name": "default", "applyToProducts": ["default"] }] },
{ "name": "common", "srcPath": "./common" },
{ "name": "news", "srcPath": "./features/news" },
{ "name": "video", "srcPath": "./features/video" },
{ "name": "live", "srcPath": "./features/live" },
{ "name": "personal", "srcPath": "./features/personal" },
{ "name": "service", "srcPath": "./features/service" }
]
}
7.3 路由注册
更新 product/phone/src/main/resources/base/profile/main_pages.json:
json
{
"src": [
"pages/Index",
"pages/MainPage"
]
}
7.4 编译运行
bash
# DevEco Studio 中点击 Run 按钮
# 或使用命令行
hvigorw assembleHap
八、注意事项与最佳实践
8.1 @ComponentV2 注意事项
- 不支持 @Reusable :V2组件不能使用组件复用,用
@ObservedV2 + @Trace的细粒度更新替代 - @Param 必须初始化 :
@Param装饰的变量必须有默认值或标记@Require - @Event 替代 callback :V2中使用
@Event而非普通函数属性进行子→父通信
8.2 Tabs 底部导航注意事项
- 嵌套冲突:避免在 Tabs 内容中再次使用 Tabs 组件,会导致布局冲突
- LiveHome 解决方案:内部 Tab 栏改用自定义 Row + Text,避免嵌套
- 升级路径:待 SDK 支持后可升级为 HdsTabs 实现沉浸光感效果
8.3 NavPathStack 与 @Provider/@Consumer
- Provider必须在Consumer的祖先组件中
- key匹配 :
@Provider('pageStack')和@Consumer('pageStack')的key必须一致 - NavDestination页面 :通过
navDestination注册的页面也能通过@Consumer获取 Provider 的值
九、总结
本项目完整演示了如何在HarmonyOS 6.1环境下,使用最新的状态管理V2 和分层模块化架构构建一个新闻资讯应用。核心收获:
- 架构清晰:三层架构(产品层/特性层/公共层)各司其职
- 技术前沿:全面使用V2装饰器,代码更简洁、更新更精准
- 稳定可靠:标准Tabs组件兼容性好,避免嵌套冲突
- 路由规范:Navigation + NavPathStack + @Provider + @Param 显式传参