Tabs组件如何根据数据源动态更改TabContent数量
=============================

#### 问题现象

在应用场景中，当Tabs组件的页签数量及内容由后端动态返回且数量可变时，如何通过Tabs和TabContent实现动态渲染？

#### 效果预览

![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/05/v3/G2SHFQURQUiI_eeVq2-M6w/zh-cn_image_0000002628564674.png?HW-CC-KV=V1&HW-CC-Date=20260709T134100Z&HW-CC-Expire=86400&HW-CC-Sign=408BF585219FD4C1C651C3B2823F9812CC40E4FD542E53BCE21902EDB9B7E807 "点击放大")

#### 背景知识

*   [ForEach（循环渲染）](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-rendering-control-foreach) ：ForEach接口基于数组循环渲染，需要与容器组件配合使用，且接口返回的组件应当是允许包含在ForEach父容器组件中的子组件。
*   [选项卡（Tabs）](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-navigation-tabs) ：Tabs组件可以在一个页面内快速实现视图内容的切换，一方面提升查找信息的效率，另一方面精简用户单次获取到的信息量。
*   [@Watch装饰器（状态变量更改通知）](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-watch) ：@Watch应用于对状态变量的监听。如果开发者需要关注某个状态变量的值是否改变，可以使用@Watch为状态变量设置回调函数。

#### 解决方案

动态生成Tabs和TabContent（数量和内容由后端数据决定），核心是通过数据驱动UI，利用循环渲染（ForEach）结合后端返回的数据源实现。以下是具体实现步骤：

1.  定义数据模型：定义接收后端数据的模型，包含每个Tab的标题和对应内容数据。
    
    _//_ _定义单个Tab的数据结构_
    interface TabItem {
      id: string; _// 唯一标识_
      title: string; _// Tab标题_
      content: string; _// Tab对应的内容（可根据实际需求扩展）_
    }
    
2.  设置相关初始值：设置模拟后端返回的数据、控制添加或删除按钮状态的初始值，对模拟后端返回的数据进行监听，渲染UI。
    
    _//_ _表示添加或删减子页面的状态，true为添加，false为删减_
    updateState: boolean \= true;
    _// @watch__对count进行监听，当count发生变化，执行updateTabList()_
    @State @Watch('updateTabList') count: number \= 0;
    _//_ _模拟后端返回的数据（实际中通过http请求获取）_
    @State tabList: TabItem\[\] \= \[
      { id: '1', title: '推荐', content: '推荐内容' },
      { id: '2', title: '热点', content: '热点内容' },
    \];
    
3.  模拟后端数据更新。
    
    _//_ _模拟后端数据更新（实际中在http请求回调中执行）_
    updateTabList() {
      if (this.updateState) {
       _ // 添加子页面的操作_
        this.tabList \= \[
          ...this.tabList, _// 保留原有数据_
          { id: \`${this.count \+ 2}\`, title: \`新增Tab${this.count}\`, content: \`新增内容${this.count}\` }// 新增数据
        \];
      } else {
     _   // 删减子页面的操作_
        this.tabList.pop();
      }
    };
    
4.  使用按钮增删TabContent数据。
    
    Column() {
      Tabs() {
       _ // 循环生成TabContent，标题由每个item的title决定_
        ForEach(this.tabList, (item: TabItem) \=\> {
          TabContent() {
         _   // 每个Tab的内容，可替换为复杂组件_
            Column({ space: 10 }) {
              Text(item.content)
                .width('100%')
                .layoutWeight(1)
                .textAlign(TextAlign.Center);
           _   // 在推荐页面添加两个按钮用于添加或删除子页面_
              if (item.id \=== '1') {
                Button('添加子页面')
                  .width('80%')
                  .height(50)
                  .borderRadius(20)
                  .margin({ bottom: 16 })
                  .onClick(() \=\> {
                  _  // 先对updateState赋值_
                    this.updateState \= true;
              _      // 再对count进行操作_
                    this.count += 1;
                  });
                Button('删除子页面')
                  .width('80%')
                  .height(50)
                  .borderRadius(20)
                  .margin({ bottom: 16 })
                  .onClick(() \=\> {
                    this.updateState \= false;
                    this.count \-= 1;
                  });
              }
            }
            .width('100%')
            .height('100%');
          }
          .tabBar(item.title); _// 设置当前Tab的标题_
        }, (item: TabItem) \=\> item.id); _// 唯一键（必填，用于DiffUI）_
      }
      .width('100%')
      .height('100%');
    };
    

完整示例参考如下：

_//_ _定义单个Tab的数据结构_
interface TabItem {
  id: string; _// 唯一标识_
  title: string; _// Tab__标题_
  content: string; _// Tab对应的内容（可根据实际需求扩展）_
}

@Entry
@Component
struct DynamicTabsPage {
  _// 表示添加或删减子页面的状态，true为添加，false为删减_
  updateState: boolean \= true;
 _ // @watch对count进行监听，当count发生变化，执行updateTabList()_
  @State @Watch('updateTabList') count: number \= 0;
 _ // 模拟后端返回的数据（实际中通过http请求获取）_
  @State tabList: TabItem\[\] \= \[
    { id: '1', title: '推荐', content: '推荐内容' },
    { id: '2', title: '热点', content: '热点内容' },
  \];

 _ // 模拟后端数据更新（实际中在http请求回调中执行）_
  updateTabList() {
    if (this.updateState) {
  _    // 添加子页面的操作_
      this.tabList \= \[
        ...this.tabList, _// 保留原有数据_
        { id: \`${this.count \+ 2}\`, title: \`新增Tab${this.count}\`, content: \`新增内容${this.count}\` } _//_ _新增数据_
      \];
    } else {
    _  // 删减子页面的操作_
      this.tabList.pop();
    }
  };

  build() {
    Column() {
      Tabs() {
      _  // 循环生成TabContent，标题由每个item的title决定_
        ForEach(this.tabList, (item: TabItem) \=\> {
          TabContent() {
         _   // 每个Tab的内容，可替换为复杂组件_
            Column({ space: 10 }) {
              Text(item.content)
                .width('100%')
                .layoutWeight(1)
                .textAlign(TextAlign.Center);
             _ // 在推荐页面添加两个按钮用于添加或删除子页面_
              if (item.id \=== '1') {
                Button('添加子页面')
                  .width('80%')
                  .height(50)
                  .borderRadius(20)
                  .margin({ bottom: 16 })
                  .onClick(() \=\> {
                  _  // 先对updateState赋值_
                    this.updateState \= true;
                _    // 再对count进行操作_
                    this.count += 1;
                  });
                Button('删除子页面')
                  .width('80%')
                  .height(50)
                  .borderRadius(20)
                  .margin({ bottom: 16 })
                  .onClick(() \=\> {
                    this.updateState \= false;
                    this.count \-= 1;
                  });
              }
            }
            .width('100%')
            .height('100%');
          }
          .tabBar(item.title); _// 设置当前Tab的标题_
        }, (item: TabItem) \=\> item.id); _//_ _唯一键（必填，用于DiffUI）_
      }
      .width('100%')
      .height('100%');
    };
  }
}