目录

@Computed装饰器:计算属性

当开发者使用相同的计算逻辑重复绑定在UI上时,为了防止重复计算,可以使用@Computed计算属性。计算属性中依赖的状态变量变化时,只会计算一次。这解决了UI多次重用该属性导致的重复计算和性能问题。如下面例子。

@Computed
get sum() {
  return this.count1 + this.count2 + this.count3;
}
Text(`${this.count1 + this.count2 + this.count3}`) // 计算this.count1 + this.count2 + this.count3
Text(`${this.count1 + this.count2 + this.count3}`) // 重复计算this.count1 + this.count2 + this.count3
Text(`${this.sum}`) // 读取@Computed sum的缓存值,节省上述重复计算
Text(`${this.sum}`) // 读取@Computed sum的缓存值,节省上述重复计算

在阅读本文档前,建议提前阅读:@ComponentV2,@ObservedV2和@Trace,@Local。

@Computed装饰器从API version 12开始支持。

从API version 12开始,该装饰器支持在元服务中使用。

从API version 23开始,该装饰器支持在ArkTS卡片中使用。

概述

@Computed为方法装饰器,装饰getter方法。@Computed会检测被计算的属性变化,当被计算的属性变化时,@Computed只会被求解一次。不建议在@Computed中修改变量,错误的使用会导致数据无法被追踪或appfreeze等问题,详情见使用限制。

但需要注意,对于简单计算,不建议使用计算属性,因为计算属性本身也有开销。对于复杂的计算,@Computed能带来性能收益。

装饰器说明

@Computed语法:

@Computed
get varName(): T {
    return value;
}
@Computed方法装饰器 说明
支持类型 getter访问器。
从父组件初始化 禁止。
可初始化子组件 @Param。
被执行的时机

@ComponentV2中的@Computed会在自定义组件创建的时候初始化,触发@Computed计算。

@ObservedV2装饰的类中的@Computed,会在@ObservedV2装饰的类实例创建后,异步初始化,触发@Computed计算。

参与@Computed计算的状态变量被改变时,计算属性会重新计算。

是否允许赋值 @Computed装饰的属性是只读的,不允许赋值,详情见使用限制。

使用限制

使用场景

[h2]当被计算的属性变化时,@Computed装饰的getter访问器只会被求解一次

  1. 在自定义组件中使用计算属性。

    • 点击第一个Button改变lastName,触发@Computed fullName重新计算。
    • this.fullName被绑定在两个Text组件上,观察fullName日志,可以发现,计算只发生了一次。
    • 对于前两个Text组件,this.lastName + ' '+ this.firstName这段逻辑被求解了两次。
    • 如果UI中有多处需要使用this.lastName + ' '+ this.firstName这段计算逻辑,可以使用计算属性,减少计算次数。
    • 点击第二个Button,age自增,UI无变化。因为age非状态变量,只有被观察到的变化才会触发@Computed fullName重新计算。
    import { hilog } from '@kit.PerformanceAnalysisKit';
    
    const TAG = '[Sample_Textcomponent]';
    const DOMAIN = 0xF811;
    const BUNDLE = 'Textcomponent_';
    
    @Entry
    @ComponentV2
    struct CustomComponentUse {
      @Local firstName: string = 'Li';
      @Local lastName: string = 'Hua';
      age: number = 20; // 无法触发Computed
    
      @Computed
      get fullName() {
        hilog.info(DOMAIN, TAG, BUNDLE + '---------Computed----------');
        return this.firstName + ' ' + this.lastName + this.age;
      }
    
      build() {
        Column() {
          Text(this.lastName + ' ' + this.firstName)
            .fontSize(20)
            .margin(10)
          Text(this.lastName + ' ' + this.firstName)
            .fontSize(20)
            .margin(10)
          Divider()
          Text(this.fullName)
            .fontSize(20)
            .margin(10)
          Text(this.fullName)
            .fontSize(20)
            .margin(10)
          Button('changed lastName')
            .width(300)
            .margin(10)
            .onClick(() => {
              this.lastName += 'a';
            })
    
          Button('changed age')
            .width(300)
            .margin(10)
            .onClick(() => {
              this.age++;  // 无法触发Computed
            })
        }
      }
    }

    计算属性本身会带来性能开销,在实际应用开发中需要注意:

    • 对于简单的计算逻辑,可以不使用计算属性。
    • 如果计算逻辑在视图中仅使用一次,则不使用计算属性,直接求解。
  2. 在@ObservedV2装饰的类中使用计算属性。

    点击Button改变lastName,触发@Computed fullName重新计算,且只被计算一次。

    import { hilog } from '@kit.PerformanceAnalysisKit';
    
    const TAG = '[Sample_Textcomponent]';
    const DOMAIN = 0xF811;
    const BUNDLE = 'Textcomponent_';
    
    @ObservedV2
    class Name {
      @Trace public firstName: string = 'Hua';
      @Trace public lastName: string = 'Li';
    
      @Computed
      get fullName() {
        hilog.info(DOMAIN, TAG, BUNDLE + '---------Computed----------');
        return this.firstName + ' ' + this.lastName;
      }
    }
    
    const name: Name = new Name();
    
    @Entry
    @ComponentV2
    struct ObservedV2ClassUser {
      name1: Name = name;
    
      build() {
        Column() {
          Text(this.name1.fullName)
            .fontSize(20)
            .margin(10)
          Text(this.name1.fullName)
            .fontSize(20)
            .margin(10)
          // 点击Button改变lastName,触发fullName重新计算,且只被计算一次
          Button('changed lastName')
            .width(300)
            .margin(10)
            .onClick(() => {
              this.name1.lastName += 'a';
            })
        }
        .width('100%')
      }
    }

[h2]@Computed装饰的属性可以被@Monitor监听变化

下面示例展示如何使用计算属性求解fahrenheit和kelvin,并使用@Monitor监听计算属性的变化:

[h2]@Computed装饰的属性可以初始化@Param

下面的例子使用@Computed初始化@Param。