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

#### 问题现象

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

#### 效果预览

#### 背景知识

*   ForEach（循环渲染） ：ForEach接口基于数组循环渲染，需要与容器组件配合使用，且接口返回的组件应当是允许包含在ForEach父容器组件中的子组件。
*   选项卡（Tabs） ：Tabs组件可以在一个页面内快速实现视图内容的切换，一方面提升查找信息的效率，另一方面精简用户单次获取到的信息量。
*   @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%');
    };
  }
}
```