✅ 背景说明

在企业级应用中,分页加载的列表场景非常普遍,如:

  • 消息中心分页滚动;

  • 评论列表;

  • 搜索结果加载更多。

这些页面通常要处理:

  • 首次加载 loading

  • 无数据 empty

  • 分页加载更多

  • 下拉刷新

  • 加载失败重试

手动在每个页面实现这些逻辑不仅重复,而且易出错。本篇我们将封装一个集成分页逻辑、下拉刷新、加载更多、状态展示的复用组件:PagedList


🧱 一、组件目标功能

  • 接收 fetchData(page) 异步方法;

  • 支持下拉刷新;

  • 支持上拉加载;

  • 内部维护分页与状态;

  • 集成空状态与错误状态统一展示;

  • 支持插槽渲染每行 item。


📦 二、组件结构设计

@Component
struct PagedList<T> {
  @Prop fetch: (page: number) => Promise<T[]> = async () => [];
  @Prop pageSize: number = 20;
  @Prop renderItem: (item: T) => void = () => {};
}

🧠 三、状态变量封装

@State private list: T[] = [];
@State private page: number = 1;
@State private status: 'loading' | 'success' | 'error' | 'empty' = 'loading';
@State private loadingMore: boolean = false;

🚀 四、加载与刷新逻辑实现

async aboutToAppear() {
  this.load(true);
}

private async load(refresh = false) {
  if (refresh) {
    this.page = 1;
    this.list = [];
    this.status = 'loading';
  } else {
    this.loadingMore = true;
  }

  try {
    const result = await this.fetch(this.page);
    if (refresh && result.length === 0) {
      this.status = 'empty';
    } else {
      this.status = 'success';
      this.list = [...this.list, ...result];
      this.page++;
    }
  } catch {
    this.status = 'error';
  } finally {
    this.loadingMore = false;
  }
}

📄 五、组件视图实现

build() {
  Column({ space: 10 }) {
    if (this.status === 'loading') {
      Progress().type(ProgressType.Ring)
      return;
    }

    if (this.status === 'error') {
      ResultStatus({
        status: 'error',
        message: '加载失败,请重试',
        onRetry: () => this.load(true)
      })
      return;
    }

    if (this.status === 'empty') {
      EmptyView({ type: 'empty', message: '暂无内容' })
      return;
    }

    List() {
      ListItem() {
        PullRefresh(() => this.load(true)) {
          Column() {
            ForEach(this.list, item => this.renderItem(item), item => JSON.stringify(item))

            if (this.loadingMore) {
              Text('加载中...').fontSize(12).margin({ top: 8 })
            } else {
              Button('加载更多').onClick(() => this.load(false))
            }
          }
        }
      }
    }
  }.padding(20)
}

🧪 六、父组件调用示例

@Component
struct UserList {
  async fetchPage(page: number): Promise<string[]> {
    await delay(500);
    if (page > 3) return [];
    return Array.from({ length: 10 }, (_, i) => `第${(page - 1) * 10 + i + 1}个用户`);
  }

  build() {
    PagedList<string>({
      fetch: this.fetchPage,
      renderItem: (name) => Text(name).fontSize(14).padding(10)
    })
  }
}

⚠️ 七、常见问题与处理建议

问题 原因 建议
重复加载 load() 并发调用 加入 loading 状态判断防抖
滚动位置丢失 页面切换后未缓存列表 搭配 keepAlive 保持状态
没有触底加载 未接入滚动触底事件 可扩展为 Scroll + onReachBottom

🚀 八、拓展建议

  • 支持自动滚动到底加载更多;

  • 支持分页缓存与懒加载;

  • 提供 loading 插槽自定义;

  • 提供加载完全部后的 footer 插槽;

  • 与 ResultStatus + EmptyView 完美联动作为内部子组件;


✅ 小结

本篇构建了一个高复用、功能完整的 PagedList 分页列表组件,帮助你统一处理加载逻辑、刷新控制与错误状态,极大简化分页类页面的开发复杂度。

下一篇将进入第7篇:

《构建页面级状态容器组件 PageWrapper(布局 + loading + error + slot)》

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐