一、引言

在鸿蒙(HarmonyOS)应用开发中,状态管理是构建响应式 UI 的核心。当 UI 需要展示基于多个状态变量计算得出的派生数据时,如何高效地管理这些计算逻辑,避免不必要的性能开销,是开发者需要关注的重点。

@Computed 是鸿蒙状态管理 V2 提供的方法装饰器,专门用于装饰 getter 访问器。它能自动缓存计算结果,仅在依赖的状态变量发生变化时才重新计算,从而显著提升应用性能。本文将深入介绍 @Computed 的核心概念、使用限制、实战示例以及最佳实践。

二、@Computed 装饰器简介

@Computed 是鸿蒙状态管理 V2 提供的方法装饰器,用于装饰 getter 访问器,自动缓存计算结果。当依赖的状态变量变化时,只会重新计算一次,后续访问直接返回缓存值,从而避免 UI 中多次使用相同计算逻辑带来的重复性能开销。

核心特点

  • 仅在依赖变化时重新计算:只有 @Computed 访问器中读取的 @Local@Trace 等状态变量变化时,才会触发重新计算。
  • 只读属性@Computed 装饰的属性不可赋值,也不能与 !! 双向绑定连用。
  • 只能在 @ComponentV2@ObservedV2 中使用:这是 V2 状态管理框架的一部分。

使用限制

  • 只能装饰 getter 方法,不能用于普通方法或属性。
  • 计算中不能修改依赖的变量,否则可能导致循环计算引发 appfreeze
  • 多个 @Computed 之间需警惕循环求解。

三、基础使用示例

以下示例展示了一个典型的场景:通过 firstNamelastName 组合生成 fullName,UI 中多处使用该计算值时,@Computed 只求解一次。

@Entry
@ComponentV2
struct UserProfile {
  @Local firstName: string = 'Hua';
  @Local lastName: string = 'Li';

  @Computed
  get fullName(): string {
    console.info('Computed fullName 执行');
    return this.firstName + ' ' + this.lastName;
  }

  build() {
    Column({ space: 12 }) {
      // UI 中两处使用 fullName,但 @Computed 只会计算一次
      Text('全名: ' + this.fullName)
        .fontSize(20)
      Text('欢迎, ' + this.fullName)
        .fontSize(20)

      Button('修改姓氏')
        .onClick(() => {
          this.lastName += 'a';  // 触发 fullName 重新计算
        })

      Button('修改名字')
        .onClick(() => {
          this.firstName += 'n'; // 触发 fullName 重新计算
        })
    }
    .width("100%")
  }
}

运行效果:

在这里插入图片描述

运行效果分析:

  1. 页面初始化时,fullName 会计算一次,输出日志 Computed fullName 执行
  2. 两个 Text 组件绑定了 this.fullName,但第二次及后续访问均读取缓存值,不再执行计算逻辑。
  3. 点击 修改姓氏修改名字 按钮改变状态变量时,fullName 重新计算一次,UI 同步更新。

四、进阶场景:复杂计算与列表渲染

当计算逻辑较为复杂,或需要在列表渲染中多次使用派生数据时,@Computed 的优势更加明显。

@Entry
@ComponentV2
struct ShoppingCart {
  @Local items: Item[] = [
    { name: '苹果', price: 5, count: 3 },
    { name: '香蕉', price: 3, count: 5 },
    { name: '橘子', price: 4, count: 2 },
  ];

  @Computed
  get totalPrice(): number {
    console.info('Computed totalPrice 执行');
    return this.items.reduce((sum, item) => sum + item.price * item.count, 0);
  }

  build() {
    Column({ space: 12 }) {
      List() {
        ForEach(this.items, (item: Item) => {
          ListItem() {
            Row({ space: 8 }) {
              Text(item.name).fontSize(20)
              Text(`¥${item.price}`).fontSize(20)
              Text(`x${item.count}`).fontSize(20)
              Text(`小计: ¥${item.price * item.count}`).fontSize(20)
            }
          }
        })
      }
      .height('20%')

      Divider()
      Text('总计: ¥' + this.totalPrice)
        .fontSize(20)
        .fontWeight(FontWeight.Bold)
      Text('含税预估: ¥' + (this.totalPrice * 1.13).toFixed(2))
        .fontSize(20)
    }
    .padding(20)
  }
}

interface Item {
  name: string;
  price: number;
  count: number;
}

运行效果:

在这里插入图片描述

在这个示例中,totalPrice 是一个复杂的计算(遍历数组求和)。UI 中两处使用了它(显示总计和含税预估),但 @Computed 确保只在 items 数组变化时重新计算一次。

五、什么时候用 @Computed?

场景 推荐做法
简单计算(如 a + b),UI 仅用一次 直接写表达式,无需 @Computed
复杂计算,UI 多处使用 使用 @Computed,避免重复计算
需要缓存派生状态 @Computed 自动管理依赖并缓存

六、总结

@Computed 是鸿蒙 V2 状态管理中提升性能的重要工具,适合在计算逻辑较复杂或 UI 中多次引用同一派生值的场景下使用。在简单场景中直接使用表达式即可,避免过度设计。

通过合理使用 @Computed,您可以:

  • 减少不必要的计算开销:自动缓存结果,仅在依赖变化时重新计算。
  • 提升 UI 渲染性能:避免重复执行相同的计算逻辑。
  • 保持代码简洁:将派生逻辑封装在 getter 中,使 UI 代码更清晰。

希望本文能帮助您更好地理解和应用 @Computed 装饰器,构建更高效的鸿蒙应用。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐