harmony 鸿蒙\@Computed Decorator: Computed Property
\@Computed Decorator: Computed Property
When \@Computed is used, 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.
The change of a state variable can trigger the recomputing of its associated \@Computed. Before reading this topic, you are advised to read \@ComponentV2, \@ObservedV2 and \@Trace, and \@Local.
NOTE
The \@Computed decorator is supported since API version 12.
Overview
\@Computed is a method decorator that decorates the getter method. \@Computed detects the change of the computed property. When this property changes, \@Computed is solved only once. For complex computing, \@Computed provides better performance.
Decorator Description
\@Computed syntax:
@Computed
get varName(): T {
return value;
}
\@Computed Method-Type Decorator | Description |
---|---|
Supported type | The getter accessor. |
Initialization from the parent component | Forbidden. |
Child component initialization | \@Param. |
Execution time | When \@ComponentV2 is initialized, the computed property will be triggered. When the computed value changes, the computed property will be also triggered. |
Value assignment allowed | No. @Computed decorated properties are read-only. For details, see Constraints. |
Constraints
- \@Computed is a method decorator, which can decorate only the getter method.
@Computed
get fullName() { // Correct format.
return this.firstName + ' ' + this.lastName;
}
@Computed val: number = 0; // Incorrect format. An error is reported during compilation.
@Computed
func() { // Incorrect usage. An error is reported during compilation.
}
- In the getter method decorated by \@Computed, the properties involved in computation cannot be changed.
@Computed
get fullName() {
this.lastName += 'a'; // Error. The properties involved in computation cannot be changed.
return this.firstName + ' ' + this.lastName;
}
- \@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. setter of the computed property implemented by you does not take effect, and an error is reported during compilation.
@ComponentV2
struct Child {
@Param double: number = 100;
@Event $double: (val: number) => void;
build() {
Button('ChildChange')
.onClick(() => {
this.$double(200);
})
}
}
@Entry
@ComponentV2
struct Index {
@Local count: number = 100;
@Computed
get double() {
return this.count * 2;
}
// The @Computed decorated property is read-only. The setter implemented by you does not take effect, and an error is reported during compilation.
set double(newValue : number) {
this.count = newValue / 2;
}
build() {
Scroll() {
Column({ space: 3 }) {
Text(`${this.count}`)
// Incorrect format. The @Computed decorated property method is read-only and cannot be used together with two-way binding.
Child({ double: this.double!! })
}
}
}
}
- The capability provided by \@Computed for the status management V2 can be used only in \@ComponentV2 and \@ObservedV2.
- Be cautious about loop solving when multiple \@Computed are used together.
@Local a : number = 1;
@Computed
get b() {
return this.a + ' ' + this.c; // Incorrect format. A loop b -> c -> b exists.
}
@Computed
get c() {
return this.a + ' ' + this.b; // Incorrect format. A loop c -> b -> c exists.
}
Use Scenarios
\@Computed Decorated getter Accessor Is Solved Only Once Upon Property Change
Using computed property in a custom component.
Click the first button to change the value of lastName, triggering \@Computed fullName recomputation.
The this.fullName is bound to two Text components. The fullName log shows that the computation occurs only once.
For the first two Text components, the this.lastName +’ ‘+ this.firstName logic is solved twice.
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.
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.
@Entry
@ComponentV2
struct Index {
@Local firstName: string = 'Li';
@Local lastName: string = 'Hua';
age: number = 20; // Computed cannot be triggered.
@Computed
get fullName() {
console.info("---------Computed----------");
return this.firstName + ' ' + this.lastName + this.age;
}
build() {
Column() {
Text(this.lastName + ' ' + this.firstName)
Text(this.lastName + ' ' + this.firstName)
Divider()
Text(this.fullName)
Text(this.fullName)
Button('changed lastName').onClick(() => {
this.lastName += 'a';
})
Button('changed age').onClick(() => {
this.age++; // Computed cannot be triggered.
})
}
}
}
Note that the computed property itself has performance overhead. In actual application development: - For the preceding simple computation, computed property is not needed. - If the computed property is used only once in the view, you can solve the problem directly.
- Using computed property in classes decorated by \@ObservedV2.
- Click the button to change the value of lastName and the \@Computed fullName will be recomputed only once.
@ObservedV2
class Name {
@Trace firstName: string = 'Li';
@Trace lastName: string = 'Hua';
@Computed
get fullName() {
console.info('---------Computed----------');
return this.firstName + ' ' + this.lastName;
}
}
const name: Name = new Name();
@Entry
@ComponentV2
struct Index {
name1: Name = name;
build() {
Column() {
Text(this.name1.fullName)
Text(this.name1.fullName)
Button('changed lastName').onClick(() => {
this.name1.lastName += 'a';
})
}
}
}
\@Monitor can Listen for the Changes of the \@Computed Decorated Properties
The following example shows how to solve fahrenheit and kelvin by using computed property. - Click “-” to run the logic celsius– -> fahrenheit -> kelvin. The change of kelvin triggers the onKelvinMonitor. - Click “+” to run the logic celsius++ -> fahrenheit -> kelvin. The change of kelvin triggers the onKelvinMonitor.
@Entry
@ComponentV2
struct MyView {
@Local celsius: number = 20;
@Computed
get fahrenheit(): number {
return this.celsius * 9 / 5 + 32; // C -> F
}
@Computed
get kelvin(): number {
return (this.fahrenheit - 32) * 5 / 9 + 273.15; // F -> K
}
@Monitor("kelvin")
onKelvinMonitor(mon: IMonitor) {
console.log("kelvin changed from " + mon.value()?.before + " to " + mon.value()?.now);
}
build() {
Column({ space: 20 }) {
Row({ space: 20 }) {
Button('-')
.onClick(() => {
this.celsius--;
})
Text(`Celsius ${this.celsius.toFixed(1)}`).fontSize(50)
Button('+')
.onClick(() => {
this.celsius++;
})
}
Text(`Fahrenheit ${this.fahrenheit.toFixed(2)}`).fontSize(50)
Text(`Kelvin ${this.kelvin.toFixed(2)}`).fontSize(50)
}
.width('100%')
}
}
\@Computed Decorated Properties Initialize \@Param
The following example uses \@Computed to initialize \@Param. - Click Button(’-‘) and Button(’+‘) to change the offering quantity. The quantity is decorated by \@Trace and can be observed when it is changed. - 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. - The change of total and qualifiesForDiscount triggers the update of the Text component corresponding to the Child component.
@ObservedV2
class Article {
@Trace quantity: number = 0;
unitPrice: number = 0;
constructor(quantity: number, unitPrice: number) {
this.quantity = quantity;
this.unitPrice = unitPrice;
}
}
@Entry
@ComponentV2
struct Index {
@Local shoppingBasket: Article[] = [new Article(1, 20), new Article(5, 2)];
@Computed
get total(): number {
return this.shoppingBasket.reduce((acc: number, item: Article) => acc + (item.quantity * item.unitPrice), 0);
}
@Computed
get qualifiesForDiscount(): boolean {
return this.total >= 100;
}
build() {
Column() {
Text(`Shopping List: `).fontSize(30)
ForEach(this.shoppingBasket, (item: Article) => {
Row() {
Text(`unitPrice: ${item.unitPrice}`)
Button('-').onClick(() => {
if (item.quantity > 0) {
item.quantity--;
}
})
Text(`quantity: ${item.quantity}`)
Button('+').onClick(() => {
item.quantity++;
})
}
Divider()
})
Child({ total: this.total, qualifiesForDiscount: this.qualifiesForDiscount })
}.alignItems(HorizontalAlign.Start)
}
}
@ComponentV2
struct Child {
@Param total: number = 0;
@Param qualifiesForDiscount: boolean = false;
build() {
Row() {
Text(`Total: ${this.total} `).fontSize(30)
Text(`Discount: ${this.qualifiesForDiscount} `).fontSize(30)
}
}
}
你可能感兴趣的鸿蒙文章
harmony 鸿蒙\@AnimatableExtend Decorator: Definition of Animatable Attributes
harmony 鸿蒙Application State Management Overview
harmony 鸿蒙AppStorage: Storing Application-wide UI State
harmony 鸿蒙Basic Syntax Overview
harmony 鸿蒙\@Builder Decorator: Custom Builder Function
harmony 鸿蒙\@BuilderParam Decorator: Referencing the \@Builder Function
harmony 鸿蒙Creating a Custom Component
harmony 鸿蒙Mixing Use of Custom Components
harmony 鸿蒙Constraints on Access Modifiers of Custom Component Member Variables
- 所属分类: 后端技术
- 本文标签:
热门推荐
-
2、 - 优质文章
-
3、 gate.io
-
8、 golang
-
9、 openharmony
-
10、 Vue中input框自动聚焦