通用组件
概述
通用组件模块提供了一系列可复用的 UI 组件,用于简化页面开发。
组件列表
| 组件 | 职责 | 使用场景 |
|---|---|---|
| Empty | 空状态组件 | 数据为空时显示 |
| EmptyData | 数据为空 | 列表无数据 |
| EmptyError | 错误状态 | 请求失败 |
| EmptyNetwork | 网络错误 | 网络异常 |
| Loading | 加载指示器 | 数据加载中 |
| PageLoading | 页面加载 | 整页加载 |
| AppNavDestination | 页面导航容器,统一生命周期转发与标题栏配置 | 页面导航 |
| BaseNetWorkView | 单次请求状态视图(加载/错误/成功) | 网络请求页面 |
| BaseNetWorkListView | 列表请求状态视图(加载/错误/空/成功) | 列表页面 |
| RefreshLayout | 下拉刷新与上拉加载容器 | 下拉刷新/上滑加载 |
空状态组件
Empty
通用空状态组件:
typescript
import { Empty } from "@core/components";
Empty({
image: $r("app.media.empty"),
title: "暂无数据",
description: "当前没有任何内容"
});EmptyData
数据为空组件:
typescript
import { EmptyData } from "@core/components";
EmptyData({
onRetry: () => {
// 重试逻辑
}
});EmptyError
错误状态组件:
typescript
import { EmptyError } from "@core/components";
EmptyError({
message: "加载失败",
onRetry: () => {
// 重试逻辑
}
});EmptyNetwork
网络错误组件:
typescript
import { EmptyNetwork } from "@core/components";
EmptyNetwork({
onRetry: () => {
// 重试逻辑
}
});加载组件
Loading
加载指示器:
typescript
import { Loading } from "@core/components";
Loading();PageLoading
页面加载组件:
typescript
import { PageLoading } from "@core/components";
PageLoading({
message: "加载中..."
});网络请求视图
BaseNetWorkView
配合 BaseNetWorkViewModel 使用的视图组件:
typescript
import { BaseNetWorkView } from "@core/components";
import { BaseNetWorkViewModel } from "@core/base";
@Entry
@Component
struct MyPage {
@Local viewModel: MyViewModel = new MyViewModel();
build() {
BaseNetWorkView({
viewModel: this.viewModel,
contentBuilder: () => {
// 成功状态的内容
this.buildContent();
}
});
}
@Builder
buildContent() {
Column() {
Text(this.viewModel.data?.name);
}
}
}BaseNetWorkListView
配合 BaseNetWorkListViewModel 使用的列表视图组件:
typescript
import { BaseNetWorkListView } from "@core/components";
import { BaseNetWorkListViewModel } from "@core/base";
@Entry
@Component
struct MyListPage {
@Local viewModel: MyListViewModel = new MyListViewModel();
build() {
BaseNetWorkListView({
viewModel: this.viewModel,
itemBuilder: (item: MyItem) => {
// 列表项内容
this.buildItem(item);
}
});
}
@Builder
buildItem(item: MyItem) {
ListItem() {
Text(item.name);
}
}
}导航组件
AppNavDestination
导航目标组件,用于页面导航:
typescript
import { AppNavDestination } from "@core/components";
@Entry
@Component
struct MyPage {
build() {
AppNavDestination({
title: "页面标题",
content: () => {
this.buildContent();
}
});
}
@Builder
buildContent() {
Column() {
// 页面内容
}
}
}刷新组件
RefreshLayout
下拉刷新和上滑加载组件,基于 IBestPullRefresh 封装:
参数说明
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
loading | boolean | false | 是否处于加载状态 |
isEnableSlideUp | boolean | true | 是否启用上滑加载 |
scroller | Scroller | new Scroller() | 列表/网格使用的 scroller |
onRefresh | (direction: "pull" | "slideUp") => void | () => {} | 刷新回调 |
content | CustomBuilder | 必填 | 刷新容器内容 |
使用示例
typescript
import { RefreshLayout } from "@core/components";
@Entry
@Component
struct MyListPage {
@State loading: boolean = false;
scroller: Scroller = new Scroller();
build() {
RefreshLayout({
loading: this.loading,
scroller: this.scroller,
isEnableSlideUp: true,
onRefresh: (direction: "pull" | "slideUp") => {
this.handleRefresh(direction);
},
content: () => {
this.buildList();
}
});
}
@Builder
buildList() {
List({ scroller: this.scroller }) {
ForEach(this.dataList, (item: DataItem) => {
ListItem() {
Text(item.name);
}
});
}
}
handleRefresh(direction: "pull" | "slideUp") {
this.loading = true;
if (direction === "pull") {
// 下拉刷新逻辑
this.loadData(1);
} else {
// 上滑加载更多逻辑
this.loadMore();
}
}
async loadData(page: number) {
// 加载数据
setTimeout(() => {
this.loading = false;
}, 1000);
}
async loadMore() {
// 加载更多数据
setTimeout(() => {
this.loading = false;
}, 1000);
}
}使用示例
完整的网络请求页面
typescript
import { BaseNetWorkView } from "@core/components";
import { BaseNetWorkViewModel } from "@core/base";
@ObservedV2
class MyViewModel extends BaseNetWorkViewModel<UserInfo> {
protected requestRepository(): Promise<NetworkResult<UserInfo>> {
return this.userService.getUserInfo();
}
}
@Entry
@Component
struct UserProfilePage {
@Local viewModel: MyViewModel = new MyViewModel();
build() {
BaseNetWorkView({
viewModel: this.viewModel,
contentBuilder: () => {
this.buildContent();
}
});
}
@Builder
buildContent() {
Column() {
Text(this.viewModel.data?.name);
Text(this.viewModel.data?.email);
}
}
}完整的列表页面
typescript
import { BaseNetWorkListView } from "@core/components";
import { BaseNetWorkListViewModel } from "@core/base";
@ObservedV2
class MyListViewModel extends BaseNetWorkListViewModel<Product> {
protected requestRepository(page: number, pageSize: number):
Promise<NetworkResult<PageData<Product>>> {
return this.productService.getProductList(page, pageSize);
}
}
@Entry
@Component
struct ProductListPage {
@Local viewModel: MyListViewModel = new MyListViewModel();
build() {
BaseNetWorkListView({
viewModel: this.viewModel,
itemBuilder: (item: Product) => {
this.buildItem(item);
}
});
}
@Builder
buildItem(item: Product) {
ListItem() {
Row() {
Text(item.name);
Text(`¥${item.price}`);
}
}
}
}最佳实践
1. 使用封装组件
优先使用封装好的组件,而不是自己实现:
typescript
// 推荐
import { BaseNetWorkView } from "@core/components";
BaseNetWorkView({
viewModel: this.viewModel,
contentBuilder: () => this.buildContent()
});
// 不推荐
if (this.viewModel.uiState === BaseNetWorkUiState.LOADING) {
Loading();
} else if (this.viewModel.uiState === BaseNetWorkUiState.ERROR) {
EmptyError();
} else {
this.buildContent();
}2. 自定义空状态
可以自定义空状态的样式和内容:
typescript
EmptyData({
image: $r("app.media.custom_empty"),
title: "自定义标题",
description: "自定义描述",
onRetry: () => {
// 重试逻辑
}
});3. 组合使用
可以组合使用多个组件:
typescript
Column() {
AppNavDestination({
title: "页面标题",
content: () => {
BaseNetWorkView({
viewModel: this.viewModel,
contentBuilder: () => this.buildContent()
});
}
});
}注意事项
- 状态管理:确保 ViewModel 正确管理状态
- 性能优化:合理使用组件,避免过度渲染
- 用户体验:提供友好的错误提示和重试机制