1e41f4b71Sopenharmony_ci# \@Computed Decorator: Computed Property 2e41f4b71Sopenharmony_ci 3e41f4b71Sopenharmony_ci\@Computed decorator: Computed property means that the computation is performed only once when the value changes. It is mainly used to solve the performance problem caused by repeated computation when the UI reuses the property for multiple times. 4e41f4b71Sopenharmony_ci 5e41f4b71Sopenharmony_ci>**NOTE** 6e41f4b71Sopenharmony_ci> 7e41f4b71Sopenharmony_ci>The \@Computed decorator is supported since API version 12. 8e41f4b71Sopenharmony_ci> 9e41f4b71Sopenharmony_ci>State management V2 is still under development, and some features may be incomplete or not always work as expected. 10e41f4b71Sopenharmony_ci 11e41f4b71Sopenharmony_ci## Overview 12e41f4b71Sopenharmony_ci 13e41f4b71Sopenharmony_ci\@Computed is a method-type decorator that decorates the **getter** method. \@Computed detects the change of the computed property. When this property changes, \@Computed is solved only once. 14e41f4b71Sopenharmony_ciFor complex computing, \@Computed provides better performance. 15e41f4b71Sopenharmony_ci 16e41f4b71Sopenharmony_ci 17e41f4b71Sopenharmony_ci## Decorator Description 18e41f4b71Sopenharmony_ci\@Computed syntax: 19e41f4b71Sopenharmony_ci 20e41f4b71Sopenharmony_ci```ts 21e41f4b71Sopenharmony_ci@Computed get varName(): T { 22e41f4b71Sopenharmony_ci return value; 23e41f4b71Sopenharmony_ci} 24e41f4b71Sopenharmony_ci``` 25e41f4b71Sopenharmony_ci 26e41f4b71Sopenharmony_ci| \@Computed Method-Type Decorator| Description | 27e41f4b71Sopenharmony_ci| ------------------ | ----------------------------------------------------- | 28e41f4b71Sopenharmony_ci| Supported type | **getter** accessor.| 29e41f4b71Sopenharmony_ci| Initialization from the parent component | Forbidden.| 30e41f4b71Sopenharmony_ci| Child component initialization | \@Param | 31e41f4b71Sopenharmony_ci| Execution time | When \@ComponentV2 is initialized, the computed property will be triggered. When the computed value changes, the computed property will be also triggered.| 32e41f4b71Sopenharmony_ci 33e41f4b71Sopenharmony_ci## Constraints 34e41f4b71Sopenharmony_ci 35e41f4b71Sopenharmony_ci- When \@Computed is used to decorate the **getter** method, the computed property cannot be changed in this method. 36e41f4b71Sopenharmony_ci 37e41f4b71Sopenharmony_ci```ts 38e41f4b71Sopenharmony_ci@Computed 39e41f4b71Sopenharmony_ciget fullName() { 40e41f4b71Sopenharmony_ci this.lastName += 'a'; // error 41e41f4b71Sopenharmony_ci return this.firstName + ' ' + this.lastName; 42e41f4b71Sopenharmony_ci} 43e41f4b71Sopenharmony_ci``` 44e41f4b71Sopenharmony_ci- \@Computed cannot be used together with **!!**. That is, \@Computed decorates the **getter** accessor, which is not synchronized by the child components nor assigned a value. 45e41f4b71Sopenharmony_ci 46e41f4b71Sopenharmony_ci 47e41f4b71Sopenharmony_ci```ts 48e41f4b71Sopenharmony_ci@Computed 49e41f4b71Sopenharmony_ciget fullName() { 50e41f4b71Sopenharmony_ci return this.firstName + ' ' + this.lastName; 51e41f4b71Sopenharmony_ci} 52e41f4b71Sopenharmony_ci 53e41f4b71Sopenharmony_ciChild({ fullName: this.fullName!! }) // error 54e41f4b71Sopenharmony_ci``` 55e41f4b71Sopenharmony_ci- The capability provided by \@Computed for the status management V2 can be used only in \@ComponentV2 and \@ObservedV2. 56e41f4b71Sopenharmony_ci- Be cautious about loop solving when multiple \@Computed are used together. 57e41f4b71Sopenharmony_ci 58e41f4b71Sopenharmony_ci```ts 59e41f4b71Sopenharmony_ci@Local a : number = 1; 60e41f4b71Sopenharmony_ci@Computed 61e41f4b71Sopenharmony_ciget b() { 62e41f4b71Sopenharmony_ci return this.a + ' ' + this.c; // error: b -> c -> b 63e41f4b71Sopenharmony_ci} 64e41f4b71Sopenharmony_ci@Computed 65e41f4b71Sopenharmony_ciget c() { 66e41f4b71Sopenharmony_ci return this.a + ' ' + this.b; // error: c -> b -> c 67e41f4b71Sopenharmony_ci} 68e41f4b71Sopenharmony_ci``` 69e41f4b71Sopenharmony_ci 70e41f4b71Sopenharmony_ci## Use Scenarios 71e41f4b71Sopenharmony_ci### When the computed property changes, the **getter** accessor decorated by \@Computed is solved only once. 72e41f4b71Sopenharmony_ci1. Using Computed Property in a Custom Component 73e41f4b71Sopenharmony_ci 74e41f4b71Sopenharmony_ci- Click the first button to change the value of **lastName**, triggering **\@Computed fullName** recomputation. 75e41f4b71Sopenharmony_ci- The **this.fullName** is bound to two **Text** components. The **fullName** log shows that the computation occurs only once. 76e41f4b71Sopenharmony_ci- For the first two **Text** components, the **this.lastName +' '+ this.firstName** logic is solved twice. 77e41f4b71Sopenharmony_ci- If multiple places on the UI need to use the **this.lastName +' '+ this.firstName** computational logic, you can use the computed property to reduce the number of computation times. 78e41f4b71Sopenharmony_ci- Click the second button. The **age** increases automatically and the UI remains unchanged. Because **age** is not a state variable, only observed changes can trigger **\@Computed fullName** recomputation. 79e41f4b71Sopenharmony_ci 80e41f4b71Sopenharmony_ci```ts 81e41f4b71Sopenharmony_ci@Entry 82e41f4b71Sopenharmony_ci@ComponentV2 83e41f4b71Sopenharmony_cistruct Index { 84e41f4b71Sopenharmony_ci @Local firstName: string = 'Li'; 85e41f4b71Sopenharmony_ci @Local lastName: string = 'Hua'; 86e41f4b71Sopenharmony_ci age: number = 20; // cannot trigger Computed 87e41f4b71Sopenharmony_ci 88e41f4b71Sopenharmony_ci @Computed 89e41f4b71Sopenharmony_ci get fullName() { 90e41f4b71Sopenharmony_ci console.info("---------Computed----------"); 91e41f4b71Sopenharmony_ci return this.firstName + ' ' + this.lastName + this.age; 92e41f4b71Sopenharmony_ci } 93e41f4b71Sopenharmony_ci 94e41f4b71Sopenharmony_ci build() { 95e41f4b71Sopenharmony_ci Column() { 96e41f4b71Sopenharmony_ci Text(this.lastName + ' ' + this.firstName) 97e41f4b71Sopenharmony_ci Text(this.lastName + ' ' + this.firstName) 98e41f4b71Sopenharmony_ci Divider() 99e41f4b71Sopenharmony_ci Text(this.fullName) 100e41f4b71Sopenharmony_ci Text(this.fullName) 101e41f4b71Sopenharmony_ci Button('changed lastName').onClick(() => { 102e41f4b71Sopenharmony_ci this.lastName += 'a'; 103e41f4b71Sopenharmony_ci }) 104e41f4b71Sopenharmony_ci 105e41f4b71Sopenharmony_ci Button('changed age').onClick(() => { 106e41f4b71Sopenharmony_ci this.age++; // cannot trigger Computed 107e41f4b71Sopenharmony_ci }) 108e41f4b71Sopenharmony_ci } 109e41f4b71Sopenharmony_ci } 110e41f4b71Sopenharmony_ci} 111e41f4b71Sopenharmony_ci``` 112e41f4b71Sopenharmony_ci 113e41f4b71Sopenharmony_ciNote that the computed property itself has performance overhead. In actual application development: 114e41f4b71Sopenharmony_ci- For the preceding simple computation, computed property is not needed. 115e41f4b71Sopenharmony_ci- If the computed property is used only once in the view, you can solve the problem directly. 116e41f4b71Sopenharmony_ci 117e41f4b71Sopenharmony_ci2. Using Computed Property in Classes Decorated by \@ObservedV2 118e41f4b71Sopenharmony_ci- Click the button to change the value of **lastName** and the **\@Computed fullName** will be recomputed only once. 119e41f4b71Sopenharmony_ci 120e41f4b71Sopenharmony_ci```ts 121e41f4b71Sopenharmony_ci@ObservedV2 122e41f4b71Sopenharmony_ciclass Name { 123e41f4b71Sopenharmony_ci @Trace firstName: string = 'Li'; 124e41f4b71Sopenharmony_ci @Trace lastName: string = 'Hua'; 125e41f4b71Sopenharmony_ci 126e41f4b71Sopenharmony_ci @Computed 127e41f4b71Sopenharmony_ci get fullName() { 128e41f4b71Sopenharmony_ci console.info('---------Computed----------'); 129e41f4b71Sopenharmony_ci return this.firstName + ' ' + this.lastName; 130e41f4b71Sopenharmony_ci } 131e41f4b71Sopenharmony_ci} 132e41f4b71Sopenharmony_ci 133e41f4b71Sopenharmony_ciconst name: Name = new Name(); 134e41f4b71Sopenharmony_ci 135e41f4b71Sopenharmony_ci@Entry 136e41f4b71Sopenharmony_ci@ComponentV2 137e41f4b71Sopenharmony_cistruct Index { 138e41f4b71Sopenharmony_ci name1: Name = name; 139e41f4b71Sopenharmony_ci 140e41f4b71Sopenharmony_ci build() { 141e41f4b71Sopenharmony_ci Column() { 142e41f4b71Sopenharmony_ci Text(this.name1.fullName) 143e41f4b71Sopenharmony_ci Text(this.name1.fullName) 144e41f4b71Sopenharmony_ci Button('changed lastName').onClick(() => { 145e41f4b71Sopenharmony_ci this.name1.lastName += 'a'; 146e41f4b71Sopenharmony_ci }) 147e41f4b71Sopenharmony_ci } 148e41f4b71Sopenharmony_ci } 149e41f4b71Sopenharmony_ci} 150e41f4b71Sopenharmony_ci``` 151e41f4b71Sopenharmony_ci 152e41f4b71Sopenharmony_ci### \@Monitor can Listen for the Changes of the \@Computed Decorated Properties 153e41f4b71Sopenharmony_ciThe following example shows how to solve **fahrenheit** and **kelvin** by using computed property. 154e41f4b71Sopenharmony_ci- Click "-" to run the logic **celsius--** -> **fahrenheit** -> **kelvin**. The change of **kelvin** triggers the **onKelvinMonitor**. 155e41f4b71Sopenharmony_ci- Click "+" to run the logic **celsius++** -> **fahrenheit** -> **kelvin**. The change of **kelvin** triggers the **onKelvinMonitor**. 156e41f4b71Sopenharmony_ci 157e41f4b71Sopenharmony_ci```ts 158e41f4b71Sopenharmony_ci@Entry 159e41f4b71Sopenharmony_ci@ComponentV2 160e41f4b71Sopenharmony_cistruct MyView { 161e41f4b71Sopenharmony_ci @Local celsius: number = 20; 162e41f4b71Sopenharmony_ci 163e41f4b71Sopenharmony_ci @Computed 164e41f4b71Sopenharmony_ci get fahrenheit(): number { 165e41f4b71Sopenharmony_ci return this.celsius * 9 / 5 + 32; // C -> F 166e41f4b71Sopenharmony_ci } 167e41f4b71Sopenharmony_ci 168e41f4b71Sopenharmony_ci @Computed 169e41f4b71Sopenharmony_ci get kelvin(): number { 170e41f4b71Sopenharmony_ci return (this.fahrenheit - 32) * 5 / 9 + 273.15; // F -> K 171e41f4b71Sopenharmony_ci } 172e41f4b71Sopenharmony_ci 173e41f4b71Sopenharmony_ci @Monitor("kelvin") 174e41f4b71Sopenharmony_ci onKelvinMonitor(mon: IMonitor) { 175e41f4b71Sopenharmony_ci console.log("kelvin changed from " + mon.value()?.before + " to " + mon.value()?.now); 176e41f4b71Sopenharmony_ci } 177e41f4b71Sopenharmony_ci 178e41f4b71Sopenharmony_ci build() { 179e41f4b71Sopenharmony_ci Column({ space: 20 }) { 180e41f4b71Sopenharmony_ci Row({ space: 20 }) { 181e41f4b71Sopenharmony_ci Button('-') 182e41f4b71Sopenharmony_ci .onClick(() => { 183e41f4b71Sopenharmony_ci this.celsius--; 184e41f4b71Sopenharmony_ci }) 185e41f4b71Sopenharmony_ci 186e41f4b71Sopenharmony_ci Text(`Celsius ${this.celsius.toFixed(1)}`).fontSize(50) 187e41f4b71Sopenharmony_ci 188e41f4b71Sopenharmony_ci Button('+') 189e41f4b71Sopenharmony_ci .onClick(() => { 190e41f4b71Sopenharmony_ci this.celsius++; 191e41f4b71Sopenharmony_ci }) 192e41f4b71Sopenharmony_ci } 193e41f4b71Sopenharmony_ci 194e41f4b71Sopenharmony_ci Text(`Fahrenheit ${this.fahrenheit.toFixed(2)}`).fontSize(50) 195e41f4b71Sopenharmony_ci Text(`Kelvin ${this.kelvin.toFixed(2)}`).fontSize(50) 196e41f4b71Sopenharmony_ci } 197e41f4b71Sopenharmony_ci .width('100%') 198e41f4b71Sopenharmony_ci } 199e41f4b71Sopenharmony_ci} 200e41f4b71Sopenharmony_ci``` 201e41f4b71Sopenharmony_ci### \@Computed Decorated Properties Initialize \@Param 202e41f4b71Sopenharmony_ciThe following example shows how \@Computed Initialize \@Param. 203e41f4b71Sopenharmony_ci- Click **Button('-')** and **Button('+')** to change the offering quantity. The **quantity** is decorated by \@Trace and can be observed when it is changed. 204e41f4b71Sopenharmony_ci- The change of **quantity** triggers the recomputation of **total** and **qualifiesForDiscount**. In this way, you can get a result of the total price of the offering and the available discounts. 205e41f4b71Sopenharmony_ci- The change of **total** and **qualifiesForDiscount** triggers the update of the **Text** component corresponding to the **Child** component. 206e41f4b71Sopenharmony_ci 207e41f4b71Sopenharmony_ci```ts 208e41f4b71Sopenharmony_ci@ObservedV2 209e41f4b71Sopenharmony_ciclass Article { 210e41f4b71Sopenharmony_ci @Trace quantity: number = 0; 211e41f4b71Sopenharmony_ci unitPrice: number = 0; 212e41f4b71Sopenharmony_ci 213e41f4b71Sopenharmony_ci constructor(quantity: number, unitPrice: number) { 214e41f4b71Sopenharmony_ci this.quantity = quantity; 215e41f4b71Sopenharmony_ci this.unitPrice = unitPrice; 216e41f4b71Sopenharmony_ci } 217e41f4b71Sopenharmony_ci} 218e41f4b71Sopenharmony_ci 219e41f4b71Sopenharmony_ci@Entry 220e41f4b71Sopenharmony_ci@ComponentV2 221e41f4b71Sopenharmony_cistruct Index { 222e41f4b71Sopenharmony_ci @Local shoppingBasket: Article[] = [new Article(1, 20), new Article(5, 2)]; 223e41f4b71Sopenharmony_ci 224e41f4b71Sopenharmony_ci @Computed 225e41f4b71Sopenharmony_ci get total(): number { 226e41f4b71Sopenharmony_ci return this.shoppingBasket.reduce((acc: number, item: Article) => acc + (item.quantity * item.unitPrice), 0); 227e41f4b71Sopenharmony_ci } 228e41f4b71Sopenharmony_ci 229e41f4b71Sopenharmony_ci @Computed 230e41f4b71Sopenharmony_ci get qualifiesForDiscount(): boolean { 231e41f4b71Sopenharmony_ci return this.total >= 100; 232e41f4b71Sopenharmony_ci } 233e41f4b71Sopenharmony_ci 234e41f4b71Sopenharmony_ci build() { 235e41f4b71Sopenharmony_ci Column() { 236e41f4b71Sopenharmony_ci Text(`Shopping List: `).fontSize(30) 237e41f4b71Sopenharmony_ci ForEach(this.shoppingBasket, (item: Article) => { 238e41f4b71Sopenharmony_ci Row() { 239e41f4b71Sopenharmony_ci Text(`unitPrice: ${item.unitPrice}`) 240e41f4b71Sopenharmony_ci Button('-').onClick(() => { 241e41f4b71Sopenharmony_ci if (item.quantity > 0) { 242e41f4b71Sopenharmony_ci item.quantity--; 243e41f4b71Sopenharmony_ci } 244e41f4b71Sopenharmony_ci }) 245e41f4b71Sopenharmony_ci Text(`quantity: ${item.quantity}`) 246e41f4b71Sopenharmony_ci Button('+').onClick(() => { 247e41f4b71Sopenharmony_ci item.quantity++; 248e41f4b71Sopenharmony_ci }) 249e41f4b71Sopenharmony_ci } 250e41f4b71Sopenharmony_ci 251e41f4b71Sopenharmony_ci Divider() 252e41f4b71Sopenharmony_ci }) 253e41f4b71Sopenharmony_ci Child({ total: this.total, qualifiesForDiscount: this.qualifiesForDiscount }) 254e41f4b71Sopenharmony_ci }.alignItems(HorizontalAlign.Start) 255e41f4b71Sopenharmony_ci } 256e41f4b71Sopenharmony_ci} 257e41f4b71Sopenharmony_ci 258e41f4b71Sopenharmony_ci@ComponentV2 259e41f4b71Sopenharmony_cistruct Child { 260e41f4b71Sopenharmony_ci @Param total: number = 0; 261e41f4b71Sopenharmony_ci @Param qualifiesForDiscount: boolean = false; 262e41f4b71Sopenharmony_ci 263e41f4b71Sopenharmony_ci build() { 264e41f4b71Sopenharmony_ci Row() { 265e41f4b71Sopenharmony_ci Text(`Total: ${this.total} `).fontSize(30) 266e41f4b71Sopenharmony_ci Text(`Discount: ${this.qualifiesForDiscount} `).fontSize(30) 267e41f4b71Sopenharmony_ci } 268e41f4b71Sopenharmony_ci 269e41f4b71Sopenharmony_ci } 270e41f4b71Sopenharmony_ci} 271e41f4b71Sopenharmony_ci``` 272