HarmonyOS鸿蒙Next中@Builder无法在@Reusable组件中使用

HarmonyOS鸿蒙Next中@Builder无法在@Reusable组件中使用

import { ListItemDataSource } from "../../components/lazylist/Model";

interface ListItemData {
  id: string; // 唯一标识,可用于埋点
  layout: string; // 布局类型标识
  data: Object; // 实际的数据
}

[@Builder](/user/Builder)
function txt1Builder(item: ListItemData) {
  Column() {
    Text(item.data.toString())
  }
  .backgroundColor(Color.Green)
  .width('100%')
  .height(60)
}

[@Builder](/user/Builder)
function txt2Builder(item: ListItemData) {
  Column() {
    Text(item.data.toString())
  }
  .backgroundColor(Color.Red)
  .width('100%')
  .height(100)
}

@Entry
@Component
export struct MinePage {
  @State
  private dataSource: ListItemDataSource = new ListItemDataSource();

  aboutToAppear(): void {
    const list: ListItemData[] = [];
    for (let i = 0; i < 50; i++) {
      const layout = Math.random() > 0.5 ? 'txt1' : 'txt2';
      list.push({id: `${i}`, layout, data: `${layout} ${i}`});
    }

    this.dataSource.addAll(list);
  }

  build() {
    Column() {
      List({space: 4}) {
        LazyForEach(this.dataSource, (item: ListItemData, index: number) => {
          ListItem() {
            ReusableListItem({ item }).reuseId(item.layout)
          }
        }, (item: ListItemData, index: number) => item.id ?? `${index}`)
      }
        .width('100%')
        .height(0)
        .layoutWeight(1)
    }
    .width('100%')
    .height('100%')
  }
}

[@Reusable](/user/Reusable)
@Component
export struct ReusableListItem {
  @Prop item: ListItemData;

  aboutToReuse(params: Record<string, Object>): void {
    const item = params.item as ListItemData ?? this.item;
    this.item = { id: item.id, layout: item.layout, data: item.data };
  }

  build() {
    if (this.item.layout == 'txt1') {
      txt1Builder(this.item);
    } else {
      txt2Builder(this.item);
    }
  }
}

为什么ReusableListItem传入builder(如txt1Builder),会导致页面列表元素复用错乱(怀疑是界面未实际刷新)。但改成下面这种,则可以正确复用:

[@Reusable](/user/Reusable)
@Component
export struct ReusableListItem {
  @Prop item: ListItemData;

  aboutToReuse(params: Record<string, Object>): void {
    const item = params.item as ListItemData ?? this.item;
    this.item = { id: item.id, layout: item.layout, data: item.data };
  }

  build() {
    if (this.item.layout == 'txt1') {
      Column() {
        Text(this.item.data.toString())
      }
      .backgroundColor(Color.Green)
      .width('100%')
      .height(60)
      // txt1Builder(this.item);
    } else {
      Column() {
        Text(this.item.data.toString())
      }
      .backgroundColor(Color.Red)
      .width('100%')
      .height(100)
      // txt2Builder(this.item);
    }
  }
}

更多关于HarmonyOS鸿蒙Next中@Builder无法在@Reusable组件中使用的实战教程也可以访问 https://www.itying.com/category-93-b0.html

4 回复

这是因为目前ArkUI 框架的设计有所限制:

1、@Reusable装饰器仅用于自定义组件。

2、@Reusable不支持跟@ComponentV2搭配使用,@ComponentV2组件复用推荐@ReusableV2装饰器

【详情可查看官网指南说明:限制条件

**不支持**的场景有如下:

1. BuilderNode 的子自定义组件不支持 @Reusable

  • 当 BuilderNode 的子节点是自定义组件时,该自定义组件不能使用 [@Reusable](/user/Reusable) 装饰器,否则会导致应用程序触发 JSCrash。
  • 如果需要在 BuilderNode 中使用 [@Reusable](/user/Reusable) 装饰器,应使用一个普通自定义组件包裹该可复用组件。

2. ComponentContent 不支持传入 @Reusable 装饰的自定义组件

  • ComponentContent 底层是 BuilderNode,而 BuilderNode 不支持传入带有 [@Reusable](/user/Reusable) 注解的自定义组件。
  • 如果尝试传入,会导致直接崩溃(crash)。

总结

  • 直接混用[@Builder](/user/Builder) 函数不能被 [@Reusable](/user/Reusable) 装饰。
  • 间接使用:在 BuilderNode 或 ComponentContent 中,其子自定义组件不能使用 [@Reusable](/user/Reusable),否则会导致运行时报错或崩溃。
  • 替代方案:如果需要在 BuilderNode 中使用可复用组件,应通过一个普通自定义组件包裹 [@Reusable](/user/Reusable) 组件来实现。

更多关于HarmonyOS鸿蒙Next中@Builder无法在@Reusable组件中使用的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


@Reusable装饰器

@Reusable装饰器标记的自定义组件支持视图节点、组件实例和状态上下文的复用,避免重复创建和销毁,提升性能。仅用于自定义组件,与@Component装饰器结合使用。

@Reusable标识之后,在组件上下树时ArkUI框架会调用该组件的aboutToReuse方法和aboutToRecycle方法,大部分代码都集中在这两个生命周期方法中,而@Builder严格禁止在其内部定义状态变量或使用生命周期函数,故不适用。

在HarmonyOS Next中,@Builder装饰器目前不支持在@Reusable装饰的组件内部使用。这是因为@Reusable组件的复用机制与@Builder的动态构建特性存在底层逻辑冲突,会导致渲染异常或编译错误。开发者需要避免在标记为@Reusable的组件内定义或调用@Builder函数。

在HarmonyOS Next中,@Reusable组件的复用机制与@Builder的编译优化存在不兼容性,导致您遇到的复用错乱问题。

核心原因在于:

  1. @Reusable组件的复用机制@Reusable组件通过aboutToReuse方法更新组件状态,但组件的UI结构(build函数)在首次创建后会被缓存。复用时会直接使用缓存的UI结构,仅通过aboutToReuse更新数据。

  2. @Builder的编译行为@Builder函数在编译时会被内联展开。当您在@Reusable组件的build函数中调用@Builder时,这个调用关系在编译时被固化。即使aboutToReuse更新了数据,由于UI结构缓存,@Builder函数可能无法正确响应新的数据变化。

  3. 直接内联UI的优势:您第二种写法之所以正确,是因为将UI结构直接内联在build函数中。这样在组件复用时,虽然UI结构被缓存,但数据绑定是直接关联到this.item的,当aboutToReuse更新this.item后,UI能正确响应。

解决方案: 避免在@Reusable组件的build函数中调用@Builder函数。应该将UI逻辑直接内联在build函数中,或者使用条件渲染语句(if/else)直接构建不同的UI分支。

这是当前框架的设计约束,@Reusable组件需要明确的、静态可分析的UI结构才能保证正确的复用行为。

回到顶部