# TabContent预加载问题

#### 问题现象

在aboutToAppear回调中使用preloadItems方法来实现TabContent组件的预加载，结果未生效，为什么？如何解决？

#### 背景知识

*   aboutToAppear函数在创建自定义组件的新实例后，在执行其build()函数之前执行，早于Tabs组件的渲染。
*   preloadItems是控制器TabsController的一个方法，能够控制Tabs预加载指定子节点。调用该接口后会一次性加载所有指定的子节点，预加载到不存在的索引时就会报错。
*   Router路由与导航Navigation各自的生命周期不同，具体情况如下图：
    

#### 问题定位

在Tabs组件中，将预加载方法preloadItems在aboutToAppear方法中直接调用，由于aboutToAppear会在Tabs组件完成渲染前就被调用，Tabs组件的索引也就获取不到，因此导致preloadItems方法预加载了不存在的索引，从而预加载失败并且产生报错。

#### 分析结论

预加载方法preloadItems在aboutToAppear回调中被直接调用，导致预加载了不存在的索引，从而致使预加载失败。

#### 修改建议

aboutToAppear被调用的时候Tabs组件尚未完成渲染，导致Tabs的预加载方法加载不存在的索引，从而预加载失败，使用执行时间相对靠后的**生命周期**执行预加载方法即可预防问题，方案如下：

*   **场景一**：使用Router页面生命周期函数onPageShow。
    
    onPageShow回调在Router页面每次显示时都会触发一次，可以在该生命周期中调用预加载方法preloadItems，通过打印日志的方法来判断TabContent预加载是否成功，调用onPageShow的示例代码如下：
    
    ```
    import { BusinessError } from '@kit.BasicServicesKit';
    
    @Entry
    @Component
    struct RouterPageShowSolution {
      tabsController: TabsController = new TabsController();
      tabsData: number[] = [1, 2, 3, 4, 5];
    
      onPageShow(): void {
        this.tabsController.preloadItems([0, 1, 2, 3]).then(() => {
          console.info('onPageShow preloadItems success.');
        })
          .catch((error: BusinessError) => {
            console.error(`Failed to publish notification, error：${error}`);
          });
      }
    
      build() {
        Column() {
          Tabs({ controller: this.tabsController }) {
            ForEach(this.tabsData, (item: number) => {
              TabContent() {
                Text(item.toString());
              };
            });
          };
        }.height('100%').width('100%');
      }
    }
    ```
    
*   **场景二**：使用组件挂载事件onAppear。
    
    onAppear回调在组件挂载显示后触发，可以在该生命周期中调用预加载方法preloadItems，通过打印日志的方法来判断TabContent预加载是否成功，调用onAppear的示例代码如下：
    
    ```
    import { BusinessError } from '@kit.BasicServicesKit';
    
    @Entry
    @Component
    struct NavAppearSolution {
      pathStack: NavPathStack = new NavPathStack();
      tabsController: TabsController = new TabsController();
      tabsData: number[] = [1, 2, 3, 4, 5];
    
      build() {
        Navigation(this.pathStack) {
          Tabs({ controller: this.tabsController }) {
            ForEach(this.tabsData, (item: number) => {
              TabContent() {
                Text(item.toString());
              };
            });
          };
        }.height('100%').width('100%')
        .onAppear(() => {
          this.tabsController.preloadItems([0, 1, 2, 3])
            .then(() => {
              console.info('onAppear preloadItems success.');
            })
            .catch((error: BusinessError) => {
              console.error(`Failed to publish notification, error：${error}`);
            });
        });
      }
    }
    ```
    

#### 常见FAQ

Q：Tabs组件预加载生效之后会调用aboutToAppear方法吗？

A：被预加载的TabContent中的对应控件的aboutToAppear方法会被调用，可以添加日志来验证。

Q：有没有办法能够在aboutToAppear回调里进行预加载？

A：确认TabContent创建完成后再进行Tabs组件的预加载，可以通过同步获取数据再进行预加载。

Q：为什么页面组件在TabContent加载的时候只调用aboutToAppear，不调用onPageShow？

A：onPageShow是页面级的生命周期，Tabs切换触发的应该是子组件组件级的生命周期aboutToAppear。

#### 总结

由于aboutToAppear回调会在执行其build()函数之前执行，此时preloadItems方法让未完成渲染的Tabs组件去预加载子组件会失败，本文主要提供了两种方案，onPageShow回调和onAppear回调都能够在Tabs渲染完成之后调用。