【共创季稿事节】HarmonyOS 6.1 UIAbility全生命周期解析:从启动到销毁的电商多页面实战
承接上篇《应用级状态管理:AppStorage与PersistentStorage实战》,本文解决电商App最核心的多页面交互问题:从商品列表页跳转到详情页、再跳转到购物车页,如何实现页面跳转、参数传递、页面栈管理?为什么点击商品卡片后没有打开新页面?为什么返回时购物车数据没有更新?这些问题都指向Stage模型的核心——UIAbility的生命周期与路由机制。所有结论均经过API23环境验证,附赶稿专用浏览器模拟方案。
一、前言:从"单页面"到"多页面"的必然跨越
上篇写完应用级状态管理后,我尝试给电商Demo加一个"商品详情页":点击列表页的商品卡片,跳转到详情页展示完整参数,再点击"加入购物车"返回列表页。结果一运行就踩了三个坑:
-
点击卡片没反应,报
Router page not found错误 -
详情页能打开,但接收不到商品ID参数
-
返回列表页后,购物车数量没更新
查了官方文档才发现,HarmonyOS 6.1的Stage模型和之前的FA模型完全不同:页面跳转不是靠组件自己,而是靠UIAbility承载的路由机制。UIAbility是应用的"功能单元",一个UIAbility可以包含多个页面,而页面路由是UIAbility内部的导航逻辑。
本文将基于电商场景,把UIAbility的生命周期、页面路由、参数传递彻底拆透,帮你避开90%的新手坑。
二、核心概念辨析(官方没讲透的3组区别)
很多新手会把UIAbility、Page、Window搞混,甚至和Android的Activity划等号,我们先把边界理清楚:
2.1 UIAbility vs Page vs Window
|
维度 |
UIAbility(应用组件) |
Page(页面) |
Window(窗口) |
|---|---|---|---|
|
定位 |
应用功能的独立单元,拥有独立的生命周期 |
UIAbility内的单个交互界面 |
UIAbility的显示载体,负责绘制UI |
|
生命周期 |
独立管理: |
依附于UIAbility: |
依附于UIAbility: |
|
适用场景 |
电商模块、支付模块、用户中心等独立功能 |
商品列表页、详情页、购物车页等单个界面 |
悬浮窗、分屏窗口、全屏窗口等显示形态 |
|
启动方式 |
通过 |
通过 |
由系统自动创建,无需手动管理 |
2.2 Stage模型路由 vs FA模型路由
|
维度 |
Stage模型(API 10+,当前主流) |
FA模型(已逐步废弃) |
|---|---|---|
|
路由API |
|
|
|
页面管理 |
基于页面栈,支持 |
基于Ability栈,跳转逻辑复杂 |
|
参数传递 |
通过 |
通过 |
|
适用场景 |
所有新开发的应用 |
旧应用兼容 |
💡 核心认知:我们平时写的
Index.ets、DetailPage.ets都是Page(页面),它们必须依附于一个UIAbility(默认是EntryAbility)才能运行。页面跳转的本质是在同一个UIAbility内切换不同的Page。
三、代码实现(API23 电商多页面实战)
我们基于上篇的电商列表页改造,新增商品详情页,实现完整的多页面跳转逻辑。
3.1 第一步:注册页面路径(新手必踩坑!)
所有页面必须在main_pages.json中注册,否则路由会报Page not found错误。
打开entry/src/main/resources/base/profile/main_pages.json:
{
"src": [
"pages/Index", // 商品列表页(已有)
"pages/DetailPage" // 新增:商品详情页
]
}
3.2 第二步:修改商品列表页(Index.ets,添加跳转逻辑)
沿用上篇的Index.ets,新增点击商品卡片跳转详情页的逻辑:
import { router } from '@kit.ArkUI'
import { GoodsItem } from './GoodsItem'
import { GlobalState } from '../common/GlobalState'
import { GoodsBean } from './GoodsItem'
@Entry
@Component
struct Index {
@StorageLink('cartCounts') counts: number[] = [0, 0, 0]
@StorageLink('pausedId') pausedId: number = -1
@State goodsList: GoodsBean[] = [
{ id: 1, title: '【API23新特性】HarmonyOS 6.1 定制款超长商品标题...', price: 5999 },
{ id: 2, title: '华为Mate 60 Pro 先锋计划 全网通智能手机...', price: 6999 },
{ id: 3, title: '鸿蒙智联认证 智能穿戴手表 长续航蓝牙通话...', price: 1299 }
]
// 跳转到商品详情页
goToDetail(goodsId: number): void {
router.pushUrl({
url: 'pages/DetailPage', // 必须和main_pages.json里的路径一致
params: {
goodsId: goodsId, // 传递商品ID作为参数
from: 'Index' // 传递来源页面,方便埋点/逻辑判断
}
}, router.RouterMode.Standard, (err) => {
if (err) {
console.error('跳转详情页失败:', err.message)
}
})
}
get totalPrice(): number {
return GlobalState.calcTotalPrice(this.goodsList)
}
build() {
Column() {
Text('电商商品列表页 (UIAbility多页面实战)')
.fontSize(22)
.fontWeight(FontWeight.Bold)
.margin({ top: 20, bottom: 20 })
List() {
ForEach(this.goodsList, (item: GoodsBean, index: number) => {
ListItem() {
// 给商品卡片添加点击事件,跳转到详情页
GoodsItem({
goods: item,
pausedId: $this.pausedId,
count: $this.counts[index]
})
.onClick(() => {
this.goToDetail(item.id) // 点击卡片触发跳转
})
}
}, (item: GoodsBean) => item.id.toString())
}
.width('100%')
.layoutWeight(1)
// 底部结算栏(点击跳转到购物车页,后续实现)
Row() {
Text(`总价:¥${this.totalPrice}`)
.fontSize(20)
.fontColor('#FF0000')
.fontWeight(FontWeight.Bold)
Blank()
Button('去结算')
.backgroundColor('#0A59F7')
.fontColor(Color.White)
.onClick(() => {
// 后续实现跳转到结算页
console.log('跳转到结算页')
})
}
.width('100%')
.padding(20)
.backgroundColor('#f8f9fa')
}
.width('100%')
.height('100%')
.backgroundColor('#f0f0f0')
}
}
3.3 第三步:新增商品详情页(DetailPage.ets)
创建entry/src/main/ets/pages/DetailPage.ets,实现详情页逻辑:
import { router } from '@kit.ArkUI'
import { GlobalState } from '../common/GlobalState'
@Entry
@Component
struct DetailPage {
@State goodsId: number = 0 // 商品ID,从列表页传递过来
@State goodsTitle: string = ''
@State goodsPrice: number = 0
@StorageLink('cartCounts') counts: number[] = [0, 0, 0]
aboutToAppear(): void {
// 接收从列表页传递过来的参数
const params = router.getParams()
if (params) {
this.goodsId = params['goodsId'] as number || 0
this.goodsTitle = params['goodsTitle'] as string || ''
this.goodsPrice = params['goodsPrice'] as number || 0
console.log('详情页接收到参数:', params)
}
}
// 加入购物车
addToCart(): void {
if (this.goodsId > 0) {
const index = this.goodsId - 1 // 商品ID从1开始,数组索引从0开始
GlobalState.updateCartCount(index, this.counts[index] + 1)
// 返回列表页
router.back()
}
}
build() {
Column() {
// 顶部导航栏
Row() {
Button('< 返回')
.backgroundColor('transparent')
.fontColor('#0A59F7')
.onClick(() => {
router.back() // 返回上一页
})
Blank()
Text('商品详情页')
.fontSize(18)
.fontWeight(FontWeight.Bold)
Blank()
}
.width('100%')
.padding(15)
.backgroundColor('#FFFFFF')
// 商品信息
Column({ space: 20 }) {
// 商品图片占位
Column()
.width('100%')
.height(250)
.backgroundColor('#e8f4ff')
.borderRadius(12)
.margin({ top: 20 })
Text(this.goodsTitle || `商品${this.goodsId}详情`)
.fontSize(20)
.fontWeight(FontWeight.Bold)
.margin({ top: 20 })
Text(`¥${this.goodsPrice}`)
.fontSize(24)
.fontColor('#FF0000')
.fontWeight(FontWeight.Bold)
Text('这是商品详情介绍,包含参数、售后、评价等信息...')
.fontSize(14)
.fontColor('#666666')
.margin({ top: 10 })
// 加入购物车按钮
Button('加入购物车')
.width('90%')
.height(50)
.backgroundColor('#0A59F7')
.fontColor(Color.White)
.fontSize(18)
.onClick(() => {
this.addToCart()
})
}
.padding(20)
.alignItems(HorizontalAlign.Start)
}
.width('100%')
.height('100%')
.backgroundColor('#f0f0f0')
}
}
3.4 第四步:验证UIAbility生命周期(关键调试技巧)
打开entry/src/main/ets/entryability/EntryAbility.ts,添加生命周期日志,验证执行顺序:
import { UIAbility, AbilityConstant, Want } from '@kit.AbilityKit'
import { window } from '@kit.ArkUI'
import { PersistentStorage } from '@kit.ArkUI'
export default class EntryAbility extends UIAbility {
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
console.log('EntryAbility onCreate,启动参数:', want.parameters)
// 初始化PersistentStorage(上篇内容)
PersistentStorage.persistProp('cartCounts', [0, 0, 0])
PersistentStorage.persistProp('pausedId', -1)
}
onWindowStageCreate(windowStage: window.WindowStage): void {
console.log('EntryAbility onWindowStageCreate')
windowStage.loadContent('pages/Index', (err) => {
if (err.code) {
console.error('加载页面失败:', err.message)
return
}
console.log('页面加载成功')
})
}
onNewWant(want: Want): void {
// 当UIAbility已经启动,再次被调用时触发(比如从通知栏点击进入)
console.log('EntryAbility onNewWant,新参数:', want.parameters)
}
onForeground(): void {
console.log('EntryAbility onForeground,进入前台')
}
onBackground(): void {
console.log('EntryAbility onBackground,进入后台')
}
onDestroy(): void {
console.log('EntryAbility onDestroy,销毁')
}
}
生命周期执行顺序验证:
-
首次启动App:
onCreate→onWindowStageCreate→onForeground -
跳转到详情页:无UIAbility生命周期变化(页面跳转是内部行为)
-
按Home键回到桌面:
onBackground -
从桌面重新打开App:
onForeground -
划掉App后台进程:
onDestroy
四、原理剖析:为什么这样写性能最优?
4.1 页面栈的最小化刷新
router.pushUrl会将新页面压入页面栈,返回时弹出栈顶页面。ArkUI只会重建当前显示的页面,不会重建整个UIAbility,符合最小刷新原则。比如从列表页跳转到详情页,列表页的组件不会被销毁,返回时直接复用,性能极高。
4.2 单实例模式的合理使用
如果希望购物车页全局只有一个实例(不管从哪个页面跳转,都不会重复创建),可以在module.json5中配置UIAbility的启动模式:
{
"module": {
"abilities": [
{
"name": "EntryAbility",
"launchType": "singleton", // 单实例模式
"srcEntry": "./ets/entryability/EntryAbility.ets"
}
]
}
}
配置后,多次启动EntryAbility都会复用同一个实例,触发onNewWant回调,而不是重新创建,适合购物车、用户中心等全局唯一的页面。
4.3 参数传递的最优路径
-
小参数(<128KB):通过
router.pushUrl的params传递,简单直接 -
大参数/共享数据:通过
AppStorage中转,避免参数超限(呼应上篇的应用级状态管理) -
跨Ability传递:通过
want.parameters传递,适合不同UIAbility之间的数据交互
五、踩坑记录(官方文档没写的5个细节)
这些坑我花了3小时才踩明白,新手可以直接跳过:
5.1 页面路径必须和main_pages.json完全一致
❌ 错误:router.pushUrl({ url: 'pages/detail' })(少了Page后缀)
✅ 正确:router.pushUrl({ url: 'pages/DetailPage' })(和main_pages.json里的src完全一致,大小写敏感)
原因:路由系统是根据main_pages.json查找页面的,路径不匹配会直接报Page not found错误。
5.2 参数大小限制128KB
❌ 错误:传递一个10MB的商品详情对象作为params
✅ 正确:只传递商品ID,详情数据从网络/AppStorage加载
原因:params的大小限制为128KB,超过会直接抛异常。大对象必须通过AppStorage或数据库中转。
5.3 onNewWant必须重写
当UIAbility配置为singleton模式时,再次启动不会触发onCreate,而是触发onNewWant。如果需要在启动时接收新参数,必须在onNewWant中处理,否则参数会丢失。
5.4 页面返回时的参数传递
❌ 错误:从详情页返回时,用router.pushUrl跳转到列表页
✅ 正确:用router.back()返回,列表页在onPageShow中读取AppStorage里的更新数据
原因:pushUrl会创建新的页面实例,导致页面栈无限增长,最终超过32层的限制抛异常。返回时必须用router.back()。
5.5 页面栈深度限制32层
HarmonyOS的页面栈默认最多支持32层,超过会抛Stack overflow异常。因此在开发中要避免无意义的跳转,比如不要在循环中调用pushUrl,返回时一定要用back而不是pushUrl。


六、总结与延伸
本文实现了从单页面到多页面的跨越,核心收获有三个:
-
UIAbility是Stage模型的核心,承载一组相关页面,拥有独立的生命周期
-
页面路由基于页面栈,
pushUrl入栈、back出栈,避免页面栈溢出 -
参数传递要根据大小选择合适的路径,大对象必须用AppStorage中转
至此,我们已经覆盖了HarmonyOS 6.1开发的环境搭建→组件基础→状态管理→业务实战→应用级存储→多页面路由全流程。下一篇,我们将挑战Stage模型的高级特性——分布式流转,讲解如何实现跨设备(手机、平板、智慧屏)的页面接续、数据同步,让我们的电商App真正具备"万物互联"的能力。
更多推荐




所有评论(0)