鸿蒙五层架构:应用 / UIAbility / 窗口 / 页面 / 组件的关系与生命周期
适用版本:HarmonyOS NEXT / API 12+
语言:ArkTS
一、五层架构总览
鸿蒙应用从宏观到微观分为五个层级,层层包含、逐级管理:
┌─────────────────────────────────────────────┐
│ Application(应用) │
│ ┌───────────────────────────────────────┐ │
│ │ UIAbility(任务单元) │ │
│ │ ┌─────────────────────────────────┐ │ │
│ │ │ WindowStage(窗口舞台) │ │ │
│ │ │ ┌───────────────────────────┐ │ │ │
│ │ │ │ Page(页面)@Entry │ │ │ │
│ │ │ │ ┌─────────────────────┐ │ │ │ │
│ │ │ │ │ Component(组件) │ │ │ │ │
│ │ │ │ │ @Component │ │ │ │ │
│ │ │ │ └─────────────────────┘ │ │ │ │
│ │ │ └───────────────────────────┘ │ │ │
│ │ └─────────────────────────────────┘ │ │
│ └───────────────────────────────────────┘ │
└─────────────────────────────────────────────┘
| 层级 | 装饰器 / 类 | 核心职责 |
|---|---|---|
| Application | 无(系统管理) | 应用进程,全局资源和存储 |
| UIAbility | extends UIAbility |
系统调度单元,管理任务和窗口 |
| WindowStage | 系统创建,onWindowStageCreate 中使用 |
管理绘制窗口,加载根页面 |
| Page | @Entry + @Component |
路由页面的根节点 |
| Component | @Component / @ComponentV2 |
可复用 UI 单元 |
二、各层的职责与连接关系
2.1 Application → UIAbility
Application 是整个应用进程的容器,一个应用可以包含多个 UIAbility。Application 层主要提供:
- 全局状态存储 :
AppStorage、AppStorageV2、PersistenceV2 - 全局上下文 :
getContext()获取应用级 Context
UIAbility 是系统任务调度的基本单元,对应任务列表(最近任务)中的一个卡片。
typescript
// 在任意组件中获取 Application 级别上下文
const appContext = getContext(this).getApplicationContext();
// AppStorageV2:跨 UIAbility、跨页面的全局状态(推荐 V2 写法)
import { AppStorageV2 } from '@kit.ArkUI';
class UserState {
isLogin: boolean = false;
userId: string = '';
}
// 任意位置存入
AppStorageV2.connect(UserState, () => new UserState())!.isLogin = true;
// 任意位置取出(同一进程内共享)
const userState = AppStorageV2.connect(UserState, () => new UserState())!;
使用场景选择:
| 场景 | 方案 |
|---|---|
| 单任务应用(微信主界面) | 1 个 UIAbility + 多页面 |
| 需要系统任务列表多入口 | 多个 UIAbility |
| 分屏 / 多窗口 | 多个 UIAbility |
2.2 UIAbility → WindowStage
UIAbility 启动时,系统自动创建 WindowStage(窗口舞台),它是 UIAbility 拥有的窗口管理器,负责:
- 管理该 UIAbility 的主窗口
- 通过
loadContent()加载根页面 - 提供沉浸式、避让区等窗口能力
两者通过 onWindowStageCreate 生命周期回调连接:
typescript
import { UIAbility, Want, AbilityConstant } from '@kit.AbilityKit';
import { window } from '@kit.ArkUI';
export default class EntryAbility extends UIAbility {
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
// UIAbility 创建,此时 WindowStage 还不存在
// 适合做:初始化全局数据、读取持久化存储
console.info('UIAbility onCreate');
}
onWindowStageCreate(windowStage: window.WindowStage): void {
// WindowStage 创建完成,在这里加载根页面
// 适合做:初始化窗口属性、设置沉浸式
// ① 获取主窗口
const mainWindow = windowStage.getMainWindowSync();
// ② 设置沉浸式(状态栏透明)
mainWindow.setWindowLayoutFullScreen(true);
// ③ 获取安全区域高度
const avoidArea = mainWindow.getWindowAvoidArea(window.AvoidAreaType.TYPE_SYSTEM);
const topHeight = avoidArea.topRect.height;
AppStorage.setOrCreate('statusBarHeight', px2vp(topHeight));
// ④ 加载根页面(连接 WindowStage 与 Page 层)
windowStage.loadContent('pages/Index', (err) => {
if (err.code) {
console.error(`加载页面失败: ${err.message}`);
return;
}
console.info('页面加载成功');
});
}
onWindowStageDestroy(): void {
// WindowStage 即将销毁
// 适合做:释放窗口相关资源、保存页面状态
console.info('WindowStage 销毁');
}
}
2.3 WindowStage → Page
windowStage.loadContent('pages/Index') 将 pages/Index.ets(@Entry 页面)渲染到窗口中。这是 WindowStage 与 Page 层的唯一连接点。
一个 UIAbility 只有一个 WindowStage,但可以通过 Router 或 Navigation 在同一窗口内切换多个页面。
typescript
// pages/Index.ets ------ 被 WindowStage.loadContent 加载的根页面
@Entry
@ComponentV2
struct Index {
// @Entry 标志这是一个页面根节点(可被路由管理)
build() {
Navigation(router) {
Column() {
Text('首页')
}
}
.mode(NavigationMode.Stack)
.hideTitleBar(true)
}
// Page 层生命周期(仅 @Entry 页面有)
onPageShow() {
console.info('页面出现在前台');
}
onPageHide() {
console.info('页面退到后台');
}
onBackPress(): boolean {
// 返回 true 自行处理,false 执行默认返回
return false;
}
}
2.4 Page → Component
Page 是组件树的根节点,其 build() 方法中嵌套的所有自定义组件都属于 Component 层。
typescript
// Page 内包含多个 Component
@Entry
@ComponentV2
struct HomePage { // ← Page 层(@Entry)
build() {
Column() {
HeaderBar() // ← Component 层
BannerSwiper() // ← Component 层
ProductList() // ← Component 层
BottomTabBar() // ← Component 层
}
}
}
// Component 示例
@ComponentV2
struct HeaderBar { // ← Component 层(无 @Entry)
build() {
Row() {
Text('首页').fontSize(20)
Image($r('app.media.ic_search')).width(24)
}
}
// 组件生命周期(与 Page 生命周期不同)
aboutToAppear(): void {
console.info('HeaderBar 即将出现,开始初始化');
}
onDidBuild(): void {
console.info('HeaderBar 首次渲染完成');
}
aboutToDisappear(): void {
console.info('HeaderBar 即将销毁,释放资源');
}
}
三、UIAbility 完整生命周期
3.1 生命周期函数一览
冷启动(首次启动):
onCreate → onWindowStageCreate → onForeground → [用户使用中] → onBackground → onWindowStageDestroy → onDestroy
热启动(从任务列表恢复):
onNewWant → onForeground
切到后台:
onBackground
启动到后台(如:被其他应用拉起但不显示界面):
onCreate → onBackground
3.2 完整代码示例
typescript
import { UIAbility, Want, AbilityConstant } from '@kit.AbilityKit';
import { window } from '@kit.ArkUI';
export default class EntryAbility extends UIAbility {
/**
* UIAbility 第一次被创建时调用(冷启动)
* 时机:进程创建后
* 用途:初始化应用级数据,读取持久化配置
*/
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
console.info('[UIAbility] onCreate');
// ✅ 可以做:初始化 SDK、读取本地配置
// ❌ 不能做:UI 操作(窗口还未创建)
}
/**
* UIAbility 被再次拉起时调用(热启动,不重走 onCreate)
* 时机:应用已在后台,被重新激活
* 用途:根据新的 want 参数更新页面或数据
*/
onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam): void {
console.info('[UIAbility] onNewWant');
// 常见场景:从通知、小程序、分享链接跳入应用
const targetPage = want.parameters?.['targetPage'] as string;
if (targetPage) {
// 通知页面跳转(配合 router 或 EventHub)
this.context.eventHub.emit('navigateTo', { page: targetPage });
}
}
/**
* WindowStage 创建完成
* 时机:onCreate 之后(或热启动 onNewWant 后首次显示)
* 用途:加载根页面、设置窗口属性
*/
onWindowStageCreate(windowStage: window.WindowStage): void {
console.info('[UIAbility] onWindowStageCreate');
// 初始化安全区域数据
const mainWin = windowStage.getMainWindowSync();
const topArea = mainWin.getWindowAvoidArea(window.AvoidAreaType.TYPE_SYSTEM);
const bottomArea = mainWin.getWindowAvoidArea(window.AvoidAreaType.TYPE_NAVIGATION_INDICATOR);
AppStorage.setOrCreate('statusBarHeight', px2vp(topArea.topRect.height));
AppStorage.setOrCreate('bottomBarHeight', px2vp(bottomArea.bottomRect.height));
// 加载根页面
windowStage.loadContent('pages/Index');
}
/**
* UIAbility 切到前台
* 时机:onCreate/onNewWant 后,或从后台恢复
* 用途:恢复动画、重连网络、刷新数据
*/
onForeground(): void {
console.info('[UIAbility] onForeground');
// 恢复播放、刷新用户状态等
}
/**
* UIAbility 切到后台
* 时机:用户按 Home 键、切换应用
* 用途:暂停动画、保存草稿、停止定位
*/
onBackground(): void {
console.info('[UIAbility] onBackground');
// 暂停视频、上传草稿
}
/**
* WindowStage 销毁前
* 时机:UIAbility 即将销毁,窗口关闭前
* 用途:释放窗口资源、保存页面状态
*/
onWindowStageDestroy(): void {
console.info('[UIAbility] onWindowStageDestroy');
}
/**
* UIAbility 销毁
* 时机:系统回收或用户主动关闭任务
* 用途:释放全局资源、关闭长连接
*/
onDestroy(): void {
console.info('[UIAbility] onDestroy');
}
}
3.3 典型场景时序
场景一:冷启动(首次打开应用)
用户点击图标
│
▼
onCreate() ← 初始化数据
│
▼
onWindowStageCreate() ← 创建窗口 + loadContent 加载页面
│
▼
[页面 onPageShow()] ← 页面可见
│
▼
onForeground() ← UIAbility 进入前台
场景二:按 Home 键切到后台,再回来
用户按 Home 键
│
▼
[页面 onPageHide()]
│
▼
onBackground() ← 进入后台
---用户从任务列表点击回来---
onNewWant() ← 热启动(如有新 want 参数)
│
▼
onForeground() ← 恢复前台
│
▼
[页面 onPageShow()] ← 页面重新可见
场景三:应用被系统回收后重新打开
系统回收进程(低内存)
│
▼
onBackground() → onWindowStageDestroy() → onDestroy()
---用户再次点击图标---
重走冷启动流程:
onCreate() → onWindowStageCreate() → onForeground()
四、Page 生命周期
Page 生命周期只有 @Entry 装饰的组件才有,普通组件没有这三个回调。
typescript
@Entry
@ComponentV2
struct ArticleListPage {
@Local articles: Article[] = [];
/**
* 页面显示时触发
* 触发时机:
* - 首次进入该页面
* - 从下级页面返回(Router.back)
* - UIAbility 从后台恢复到前台
*/
onPageShow(): void {
console.info('[Page] onPageShow');
// 常见用途:
// 1. 检查登录状态
// 2. 刷新需要实时更新的数据(如购物车数量)
this.checkLoginAndRefresh();
}
/**
* 页面隐藏时触发
* 触发时机:
* - 跳转到下级页面(pushUrl / pushPathByName)
* - UIAbility 切到后台
*/
onPageHide(): void {
console.info('[Page] onPageHide');
// 常见用途:暂停视频、停止轮播
}
/**
* 用户点击返回键时触发
* 返回 true:自行处理,不执行默认返回
* 返回 false/undefined:执行默认返回行为
*/
onBackPress(): boolean {
console.info('[Page] onBackPress');
// 场景:表单有未保存内容,弹对话框二次确认
if (this.hasUnsavedContent) {
this.showSaveDialog();
return true; // 阻止返回
}
return false; // 允许返回
}
private hasUnsavedContent: boolean = false;
private checkLoginAndRefresh(): void { /* ... */ }
private showSaveDialog(): void { /* ... */ }
build() { /* ... */ }
}
Navigation NavDestination 的页面级生命周期(更精细)
使用 Navigation 替代 Router 时,页面是 NavDestination,生命周期更丰富:
typescript
@ComponentV2
struct OrderDetailPage {
@Param param: OrderParam = { orderId: '' };
@Consumer() stack: NavPathStack = new NavPathStack();
build() {
NavDestination() {
Column() {
Text(`订单 ${this.param.orderId}`)
}
}
/**
* 页面创建完成,可获取 NavDestinationContext
* 对应 Router 的 aboutToAppear + 获取参数
*/
.onReady((ctx: NavDestinationContext) => {
console.info('[NavDest] onReady - 页面就绪');
const param = ctx.pathInfo.param as OrderParam;
const stack = ctx.pathStack;
})
/**
* 页面出现在屏幕前台(包括从子页面返回)
* 对应 Router 的 onPageShow
*/
.onShown(() => {
console.info('[NavDest] onShown - 页面可见');
// 刷新数据
})
/**
* 页面被子页面遮挡(跳转到下一页时)
* 对应 Router 的 onPageHide
*/
.onHidden(() => {
console.info('[NavDest] onHidden - 页面不可见');
// 暂停刷新
})
/**
* 页面即将出现(动画开始前)
*/
.onWillAppear(() => {
console.info('[NavDest] onWillAppear');
})
/**
* 页面即将消失(动画开始前)
*/
.onWillDisappear(() => {
console.info('[NavDest] onWillDisappear');
})
/**
* 拦截返回键
* 返回 true:自行处理;false:执行默认返回
*/
.onBackPressed(() => {
console.info('[NavDest] onBackPressed');
return false;
})
}
}
五、Component 生命周期
组件生命周期是最细粒度的,每个 @Component / @ComponentV2 都有。
typescript
@ComponentV2
struct ProductCard {
@Param productId: string = '';
@Local productInfo: ProductInfo | null = null;
private timer: number = -1;
/**
* 组件实例创建后、build() 执行前触发
* 用途:
* - 网络请求初始数据
* - 订阅事件
* - 启动定时器
* 注意:此时 UI 还未渲染,不能操作 DOM
*/
aboutToAppear(): void {
console.info('[Component] aboutToAppear');
this.loadProduct();
this.timer = setInterval(() => {
this.refreshPrice();
}, 5000);
}
/**
* 首次 build() 完成后触发(重渲染不触发)
* 用途:需要在 UI 渲染后才能执行的操作
* - 获取组件尺寸/位置(通过 @LocalBuilder 引用)
* - 播放入场动画
*/
onDidBuild(): void {
console.info('[Component] onDidBuild - 首次渲染完成');
// 入场动画(只在首次渲染后执行)
animateTo({ duration: 300 }, () => {
this.opacity = 1;
});
}
/**
* 组件销毁前触发
* 用途:
* - 取消订阅事件
* - 清除定时器
* - 释放资源
* 警告:不能修改状态变量(特别是 @Link),会导致不稳定
*/
aboutToDisappear(): void {
console.info('[Component] aboutToDisappear');
// ✅ 可以:清理副作用
clearInterval(this.timer);
emitter.off(1001);
// ❌ 不能:this.productInfo = null; (修改状态变量)
}
@Local opacity: number = 0;
private loadProduct(): void { /* ... */ }
private refreshPrice(): void { /* ... */ }
build() {
Column() {
Text(this.productInfo?.name ?? '加载中...')
}
.opacity(this.opacity)
}
}
六、五层生命周期完整时序图
以下是从冷启动到页面渲染完成的完整调用顺序:
应用启动(冷启动)
│
├─ [UIAbility] onCreate()
│ ↓
├─ [UIAbility] onWindowStageCreate()
│ │
│ ├─ windowStage.loadContent('pages/Index')
│ │ ↓
│ │ [Component - Index] aboutToAppear() ← 根组件初始化
│ │ ↓
│ │ [Component - Index] build() ← 首次渲染
│ │ ↓
│ │ [Page - Index] onPageShow() ← 页面可见
│ │ ↓
│ │ [Component - Index] onDidBuild() ← 渲染完成
│ │ ↓
│ │ [子组件] aboutToAppear() → build() → onDidBuild()
│ │
├─ [UIAbility] onForeground() ← 进入前台
│
│
跳转到子页面(router.pushUrl 或 stack.pushPathByName)
│
├─ [Page - Index] onPageHide() ← 当前页面隐藏
│ ↓
├─ [Component - DetailPage] aboutToAppear()
│ ↓
├─ [Component - DetailPage] build()
│ ↓
├─ [Page - DetailPage] onPageShow()
│ ↓
├─ [Component - DetailPage] onDidBuild()
│
│
从子页面返回(router.back 或 stack.pop)
│
├─ [Page - DetailPage] onPageHide()
├─ [Component - DetailPage] aboutToDisappear() ← 子组件销毁
│ ↓
├─ [Page - Index] onPageShow() ← 上一页重新可见
│
│
按 Home 键切到后台
│
├─ [Page - Index] onPageHide()
│ ↓
├─ [UIAbility] onBackground()
│
│
从后台恢复
│
├─ [UIAbility] onNewWant()(如有新参数)
├─ [UIAbility] onForeground()
│ ↓
├─ [Page - Index] onPageShow()
│
│
应用被系统回收
│
├─ [UIAbility] onBackground()
├─ [UIAbility] onWindowStageDestroy()
├─ [Component - *] aboutToDisappear() ← 所有组件销毁
├─ [UIAbility] onDestroy()
七、各层之间的"胶水":上下文与共享数据
各层之间并非孤立,通过以下机制互相连接:
7.1 UIAbility ↔ Page:EventHub
typescript
// UIAbility 中发布事件(通知页面刷新)
this.context.eventHub.emit('tokenExpired');
// 页面中订阅(收到后跳转登录)
const ctx = getContext(this) as common.UIAbilityContext;
ctx.eventHub.on('tokenExpired', () => {
router.replaceUrl({ url: 'pages/LoginPage' });
});
7.2 Page ↔ Component:@Provider / @Consumer
typescript
@Entry
@ComponentV2
struct HomePage {
@Provider() currentTab: string = 'recommend'; // Page 提供状态
build() {
Column() {
TabBar() // 子组件通过 @Consumer 接收并修改
ContentArea()
}
}
}
@ComponentV2
struct TabBar {
@Consumer() currentTab: string = ''; // 直接接收 Page 的状态
build() {
Row() {
Text('推荐').onClick(() => { this.currentTab = 'recommend'; })
Text('热卖').onClick(() => { this.currentTab = 'hot'; })
}
}
}
7.3 全局共享:AppStorageV2
typescript
// 在 UIAbility.onCreate 中初始化
AppStorageV2.connect(UserState, () => new UserState());
// 在任意 Component 中读写
@ComponentV2
struct Avatar {
@Local userState: UserState = AppStorageV2.connect(UserState, () => new UserState())!;
build() {
Image(this.userState.avatar).width(40).height(40).borderRadius(20)
}
}
7.4 导航层传参:NavPathStack @Provider
typescript
@Entry
@ComponentV2
struct Index {
@Provider() stack: NavPathStack = new NavPathStack(); // Index 提供路由栈
build() {
Navigation(this.stack) { /* ... */ }
}
}
@ComponentV2
struct AnyDeepComponent {
@Consumer() stack: NavPathStack = new NavPathStack(); // 任意子组件消费
build() {
Button('跳转').onClick(() => {
this.stack.pushPathByName('DetailPage', {});
})
}
}
八、常见问题与陷阱
❌ 陷阱 1:在 aboutToDisappear 中修改状态变量
typescript
aboutToDisappear(): void {
this.data = []; // ❌ 危险!可能导致 UI 异常或崩溃
clearInterval(this.timer); // ✅ 清理副作用没问题
}
❌ 陷阱 2:在 onCreate 中操作 UI
typescript
onCreate(): void {
router.pushUrl({ url: 'pages/Login' }); // ❌ 窗口未创建,路由无效
}
// ✅ 正确:在 onWindowStageCreate 或 onForeground 中操作
onWindowStageCreate(windowStage: window.WindowStage): void {
windowStage.loadContent('pages/Index', () => {
// 加载完成后才能操作路由
});
}
❌ 陷阱 3:混淆 onPageShow 和 aboutToAppear 的触发时机
typescript
// aboutToAppear:只在组件首次创建时触发一次
// onPageShow:每次页面出现都触发(包括从子页面返回)
// 一次性初始化 → 放在 aboutToAppear
aboutToAppear(): void {
this.initData(); // 只执行一次
}
// 需要每次进入都刷新 → 放在 onPageShow
onPageShow(): void {
this.refreshCartCount(); // 每次进入都刷新购物车数量
}
❌ 陷阱 4:UIAbility 销毁后 EventHub 订阅丢失
typescript
// 订阅放在 aboutToAppear,确保页面每次创建都会重新订阅
aboutToAppear(): void {
const ctx = getContext(this) as common.UIAbilityContext;
ctx.eventHub.on('refresh', this.onRefresh);
}
aboutToDisappear(): void {
const ctx = getContext(this) as common.UIAbilityContext;
ctx.eventHub.off('refresh', this.onRefresh); // ✅ 配对取消
}
九、总结
[Application]
全局存储(AppStorageV2)、应用级 Context
│
[UIAbility] ── 生命周期:onCreate → onWindowStageCreate → onForeground
系统任务单元,通过 EventHub 与页面通信
│
[WindowStage] ── 通过 loadContent() 连接到 Page
窗口管理,沉浸式设置,安全区域获取
│
[Page / NavDestination] ── 生命周期:onPageShow / onShown
路由节点,通过 @Provider 向子组件提供状态
│
[Component] ── 生命周期:aboutToAppear → onDidBuild → aboutToDisappear
UI 单元,通过 @Consumer 消费状态,通过 emitter 通信
每层各司其职,通过 EventHub、@Provider/@Consumer、AppStorageV2、NavPathStack 有机连接,共同构成完整的鸿蒙应用架构。