HarmonyOS鸿蒙Next中swiper组件能不预加载吗

HarmonyOS鸿蒙Next中swiper组件能不预加载吗 通过设置cachedCount为0,不起作用,有办法不让他预加载左右的子组件吗,达到滑到哪才加载哪的效果

7 回复

设置 cachedCount0 不会禁用预加载。

无法通过设置 cachedCount 或其他配置实现“滑到哪才加载哪”的完全按需加载效果。

Swiper 的设计初衷就是通过预加载来保证流畅体验。

更多关于HarmonyOS鸿蒙Next中swiper组件能不预加载吗的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


swiper的cachedCount是划出去之后会销毁,如果你设置了2,他会缓存起来不让组件销毁而已,跟预加载没关系,我复制官网demo,试了一下打印组件创建生命周期,根本就不会预加载

// xxx.ets
class MyDataSource implements IDataSource {
  private list: number[] = [];

  constructor(list: number[]) {
    this.list = list;
  }

  totalCount(): number {
    return this.list.length;
  }

  getData(index: number): number {
    return this.list[index];
  }

  registerDataChangeListener(listener: DataChangeListener): void {
  }

  unregisterDataChangeListener() {
  }
}

@Entry
@Component
struct SwiperExample {
  private swiperController: SwiperController = new SwiperController();
  private data: MyDataSource = new MyDataSource([]);

  aboutToAppear(): void {
    let list: number[] = [];
    for (let i = 1; i <= 10; i++) {
      list.push(i);
    }
    this.data = new MyDataSource(list);
  }

  build() {
    Column({ space: 5 }) {
      Swiper(this.swiperController) {
        LazyForEach(this.data, (item: string) => {
          Text(item.toString())
            .width('90%')
            .height(160)
            .backgroundColor(0xAFEEEE)
            .textAlign(TextAlign.Center)
            .fontSize(30)
            .onAppear(() => {
              console.log('appear: ' + item);
            })
        }, (item: string) => item)
      }
      .cachedCount(2)
      .index(1)
      .interval(4000)
      .indicator(Indicator.digit() // 设置数字导航点样式
        .top(200)
        .fontColor(Color.Gray)
        .selectedFontColor(Color.Gray)
        .digitFont({ size: 20, weight: FontWeight.Bold })
        .selectedDigitFont({ size: 20, weight: FontWeight.Normal }))
      .loop(true)
      .duration(1000)
      .itemSpace(0)
      .displayArrow(true, false)

      Row({ space: 12 }) {
        Button('showNext')
          .onClick(() => {
            this.swiperController.showNext();
          })
        Button('showPrevious')
          .onClick(() => {
            this.swiperController.showPrevious();
          })
      }.margin(5)
    }.width('100%')
    .margin({ top: 5 })
  }
}

cke_3072.png

  1. 框架设计限制
    Swiper组件默认存在基础缓存机制,即使设置cachedCount(0)仍会保留至少当前显示项的缓存。多个案例显示,当快速滑动时系统可能无法及时销毁已滑出组件。

  2. LazyForEach特性约束
    当配合LazyForEach使用时,系统会根据滑动方向预先创建相邻索引的组件,这与数据源的IDataSource接口实现机制相关。

通过设置 .cachedCount(0) 无法完全禁用预加载。根据系统实现逻辑,当 cachedCount 设置为 0 或负数时,系统会按照默认值 1 处理,因此始终会预加载至少相邻一个子组件。这是出于基础滑动体验的保障考虑。

你可以尝试使用

动态数据加载控制

结合 onChange 事件手动控制加载时机,实现“滑到哪才加载哪”:

@State currentIndex: number = 0;

Swiper()
  .onChange((index: number) => {
    this.currentIndex = index;
    // 根据index动态加载数据,例如通过接口请求
    this.loadDataForIndex(index);
  })

【问题分析】

只在LazyForEach和开启了virtualScroll开关的Repeat中生效,生效后超出显示及缓存范围的子节点会被释放。

cacheCount:设置预加载子组件个数,以当前页面为基准,加载当前显示页面的前后个数。前面item删除,后面会向前补位。例如cachedCount=1时,会将当前显示的页面的前面一页和后面一页的子组件都预加载。

【参考代码】

import { BusinessError } from '@kit.BasicServicesKit';

// BasicDataSource实现了IDataSource接口,用于管理listener监听,以及通知LazyForEach数据更新
class BasicDataSource implements IDataSource {
  private listeners: DataChangeListener[] = [];
  private originDataArray: string[] = [];

  public totalCount(): number {
    return this.originDataArray.length;
  }

  public getData(index: number): string {
    return this.originDataArray[index];
  }

  // 该方法为框架侧调用,为LazyForEach组件向其数据源处添加listener监听
  registerDataChangeListener(listener: DataChangeListener): void {
    if (this.listeners.indexOf(listener) < 0) {
      console.info('add listener');
      this.listeners.push(listener);
    }
  }

  // 该方法为框架侧调用,为对应的LazyForEach组件在数据源处去除listener监听
  unregisterDataChangeListener(listener: DataChangeListener): void {
    const pos = this.listeners.indexOf(listener);
    if (pos >= 0) {
      console.info('remove listener');
      this.listeners.splice(pos, 1);
    }
  }

  // 通知LazyForEach组件需要重载所有子组件
  notifyDataReload(): void {
    this.listeners.forEach(listener => {
      listener.onDataReloaded();
    });
  }

  // 通知LazyForEach组件需要在index对应索引处添加子组件
  notifyDataAdd(index: number): void {
    this.listeners.forEach(listener => {
      listener.onDataAdd(index);
      // 写法2:listener.onDatasetChange([{type: DataOperationType.ADD, index: index}]);
    });
  }

  // 通知LazyForEach组件在index对应索引处数据有变化,需要重建该子组件
  notifyDataChange(index: number): void {
    this.listeners.forEach(listener => {
      listener.onDataChange(index);
      // 写法2:listener.onDatasetChange([{type: DataOperationType.CHANGE, index: index}]);
    });
  }

  // 通知LazyForEach组件需要在index对应索引处删除该子组件
  notifyDataDelete(index: number): void {
    this.listeners.forEach(listener => {
      listener.onDataDelete(index);
      // 写法2:listener.onDatasetChange([{type: DataOperationType.DELETE, index: index}]);
    });
  }

  // 通知LazyForEach组件将from索引和to索引处的子组件进行交换
  notifyDataMove(from: number, to: number): void {
    this.listeners.forEach(listener => {
      listener.onDataMove(from, to);
      // 写法2:listener.onDatasetChange(
      //         [{type: DataOperationType.EXCHANGE, index: {start: from, end: to}}]);
    });
  }

  notifyDatasetChange(operations: DataOperation[]): void {
    this.listeners.forEach(listener => {
      listener.onDatasetChange(operations);
    });
  }
}

class MyDataSource extends BasicDataSource {
  private dataArray: string[] = [];

  public totalCount(): number {
    return this.dataArray.length;
  }

  public getData(index: number): string {
    return this.dataArray[index];
  }

  public pushData(data: string): void {
    this.dataArray.push(data);
    this.notifyDataAdd(this.dataArray.length - 1);
  }
}

@Entry
@Component
struct PageCacheCount {
  @State currentIndex: number = 1;
  private swiperController: SwiperController = new SwiperController();
  private data: MyDataSource = new MyDataSource();

  aboutToAppear() {
    for (let i = 0; i <= 20; i++) {
      this.data.pushData(`Hello ${i}`);
    }
  }

  build() {
    Column() {
      Swiper(this.swiperController) {
        LazyForEach(this.data, (item: string) => {
          ListItem() {
            Row() {
              MyComponent({ txt: item })
            }.margin({ left: 10, right: 10 })
          }
        }, (item: string) => item)
      }
      .cachedCount(0)
      .width("90%")
      .height(200)
    }
    .width("100%")
    .margin(5)
  }
}

@Component
struct MyComponent {
  private txt: string = "";

  aboutToAppear(): void {
    console.info('aboutToAppear txt:' + this.txt);
  }

  build() {
    Text(this.txt)
      .textAlign(TextAlign.Center)
      .width('100%')
      .height('100%')
      .backgroundColor(0xAFEEEE)
  }
}

HarmonyOS鸿蒙Next的Swiper组件默认支持预加载机制。通过设置cachedCount属性可控制预加载数量,设为0即可禁用预加载。该属性在Swiper组件的参数中直接配置,实现方式为:cachedCount(0)。禁用后相邻页面将不会提前加载和渲染,仅当前页保持活跃状态。此配置适用于需要严格控制内存占用的场景。

在HarmonyOS Next中,Swiper组件的cachedCount属性目前不支持设置为0来完全禁用预加载。该属性用于控制预加载的页面数量,但最小值为1,无法通过配置实现滑到哪加载哪的效果。

如果希望实现按需加载,可以考虑以下替代方案:

  1. 结合LazyForEach或条件渲染,在Swiper的每个子项中动态控制内容加载时机。
  2. 监听Swiper的索引变化事件,在滑动到特定位置时再触发子组件的数据加载或渲染。

需要注意的是,Swiper本身的设计包含预加载机制以保障滑动流畅性,完全禁用可能会影响用户体验。

回到顶部