第 18 课2026.07.28
适用场景:页面需要请求网络数据、读取数据库,且希望请求实现可以复用和独立测试
第 18 课:数据服务与页面职责分离
- 日期:2026-07-28
- 课程序号:第 18 课
- 知识点:独立数据服务、页面与数据层的职责边界
- 适用场景:页面需要请求网络数据、读取数据库,且希望请求实现可以复用和独立测试
承接上一课的异步数据加载:上一课由页面同时实现 queryMessages() 和渲染逻辑;本课把数据获取下沉到服务层,页面只负责触发加载、保存页面状态和渲染结果。
核心概念
把职责分成两层:
text
页面组件:生命周期触发 -> 调用服务 -> 更新页面状态 -> 渲染 UI
数据服务:执行数据请求 -> 返回 Promise 结果服务层不操作 @State,页面也不关心数据来自网络还是数据库。这样多个页面可以复用同一个服务,替换真实数据源时也不需要改 UI 结构。
最小 ArkTS / ArkUI 示例
ts
class MessageService {
static queryMessages(): Promise<string[]> {
return Promise.resolve(['系统通知', '学习提醒']);
}
}
@Entry
@Component
struct MessagePage {
@State loading: boolean = false;
@State items: string[] = [];
@State errorMessage: string = '';
aboutToAppear(): void {
this.loadMessages();
}
async loadMessages(): Promise<void> {
this.loading = true;
this.errorMessage = '';
try {
this.items = await MessageService.queryMessages();
} catch {
this.errorMessage = '加载失败,请重试';
} finally {
this.loading = false;
}
}
build() {
Column({ space: 12 }) {
if (this.loading) {
Text('加载中...')
} else if (this.errorMessage.length > 0) {
Text(this.errorMessage)
.fontColor(Color.Red)
} else {
ForEach(this.items, (item: string) => {
Text(item)
}, (item: string) => item)
}
}
.width('100%')
.padding(20)
}
}关键边界在 MessageService.queryMessages(): Promise<string[]>:方法签名承诺异步返回字符串数组;页面通过 await 接收结果,再写入自己的 @State。服务层不直接修改页面状态。
3~5 分钟练习
给 MessageService 增加 queryUnreadCount(): Promise<number>,返回未读数量 2;页面新增 @State unreadCount,加载消息成功后同时获取并显示未读数量。
参考答案
服务层增加:
ts
static queryUnreadCount(): Promise<number> {
return Promise.resolve(2);
}页面增加状态:
ts
@State unreadCount: number = 0;在 try 中增加调用:
ts
this.items = await MessageService.queryMessages();
this.unreadCount = await MessageService.queryUnreadCount();在 build() 的 Column 中显示:
ts
Text(`未读消息:${this.unreadCount}`)与上一课的联系
上一课建立了加载、成功和失败三类页面状态;本课保留这套状态语义,只把数据获取实现移出组件。页面仍由真实请求结果驱动 UI,没有在展示层缓存、兜底或伪造状态。