• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 组件内状态变量迁移指导
2<!--Kit: ArkUI-->
3<!--Subsystem: ArkUI-->
4<!--Owner: @liwenzhen3-->
5<!--Designer: @s10021109-->
6<!--Tester: @TerryTsao-->
7<!--Adviser: @HelloCrease-->
8
9本文档主要介绍数据组件内的状态变量的迁移场景,包含以下场景。
10| V1装饰器名                | V2装饰器名                  |
11|------------------------|--------------------------|
12| [\@State](./arkts-state.md)                 | 无外部初始化:[\@Local](./arkts-new-local.md)<br/>外部初始化一次:[\@Param](./arkts-new-param.md)[\@Once](./arkts-new-once.md) |
13| [\@Prop](./arkts-prop.md)                   | [\@Param](./arkts-new-param.md)                   |
14| [\@Link](./arkts-link.md)                  | [\@Param](./arkts-new-param.md)[\@Event](./arkts-new-event.md)    |
15|  [\@ObjectLink](./arkts-observed-and-objectlink.md)           |[\@Param](./arkts-new-param.md)[\@Event](./arkts-new-event.md)                   |
16|  [\@Provide](./arkts-provide-and-consume.md)               |[\@Provider](./arkts-new-Provider-and-Consumer.md)                |
17| [\@Consume](./arkts-provide-and-consume.md)               |[\@Consumer](./arkts-new-Provider-and-Consumer.md)                |
18| [\@Watch](./arkts-watch.md)               |[\@Monitor](./arkts-new-monitor.md)                |
19| 无计算属性相关能力,需要重复计算 | [\@Computed](./arkts-new-Computed.md)                |
20
21## 各装饰器迁移示例
22
23### \@State->\@Local
24
25**迁移规则**
26
27在V1中,\@State装饰器用于装饰组件内部的状态变量,在V2中提供了\@Local作为其替代能力,但两者在观察能力和初始化规则上存在明显差异。针对不同的使用场景,迁移策略如下:
28
29- 简单类型:对于简单类型的变量,可以直接将\@State替换为\@Local。
30- 复杂类型:V1中的@State可以观察复杂对象的第一层属性变化,而V2中的\@Local只能观察对象自身的变化。如果需要追踪对象内部的属性变化,可以结合使用\@ObservedV2和\@Trace。
31- 外部初始化:V1中,\@State支持从外部传递初始值,但在V2中,\@Local禁止外部初始化。若需要从外部传递初始值,可以使用\@Param和\@Once装饰器来实现类似的效果。
32
33**示例**
34
35**简单类型**
36
37对于简单类型变量,V1的\@State可以直接替换为V2的\@Local。
38
39V1:
40
41```ts
42@Entry
43@Component
44struct Child {
45  @State val: number = 10;
46  build(){
47    Text(this.val.toString())
48  }
49}
50```
51
52V2迁移策略:直接替换。
53
54```ts
55@Entry
56@ComponentV2
57struct Child {
58  @Local val: number = 10;
59  build(){
60    Text(this.val.toString())
61  }
62}
63```
64
65**复杂类型**
66
67V1的\@State能够观察复杂对象的第一层属性变化,但V2的\@Local无法观察对象内部变化。为了解决这个问题,需要在类上添加\@ObservedV2,并在需要观察的属性上添加\@Trace。这样,框架就能追踪对象内部的属性变化。
68
69V1:
70
71```ts
72class Child {
73  value: number = 10;
74}
75
76@Component
77@Entry
78struct example {
79  @State child: Child = new Child();
80  build(){
81    Column() {
82      Text(this.child.value.toString())
83      // @State可以观察第一层变化
84      Button('value+1')
85        .onClick(() => {
86          this.child.value++;
87        })
88    }
89  }
90}
91```
92
93V2迁移策略:使用\@ObservedV2和\@Trace。
94
95```ts
96@ObservedV2
97class Child {
98  @Trace public value: number = 10;
99}
100
101@ComponentV2
102@Entry
103struct example {
104  @Local child: Child = new Child();
105  build(){
106    Column() {
107      Text(this.child.value.toString())
108      // @Local只能观察自身,需要给Child加上@ObservedV2和@Trace
109      Button('value+1')
110        .onClick(() => {
111          this.child.value++;
112        })
113    }
114  }
115}
116```
117
118**外部初始化状态变量**
119
120V1的\@State变量可以从外部初始化,V2的\@Local禁止外部初始化。为实现类似功能,需要用\@Param和\@Once代替\@State,允许外部传入初始值,并确保该值只初始化时同步一次。
121
122V1实现:
123
124```ts
125@Component
126struct Child {
127  @State value: number = 0;
128  build() {
129    Text(this.value.toString())
130  }
131}
132
133@Entry
134@Component
135struct Parent {
136  build() {
137    Column(){
138      // @State可以从外部初始化
139      Child({ value: 30 })
140    }
141  }
142}
143```
144
145V2迁移策略:使用\@Param和\@Once。
146
147```ts
148@ComponentV2
149struct Child {
150  @Param @Once value: number = 0;
151  build() {
152    Text(this.value.toString())
153  }
154}
155
156@Entry
157@ComponentV2
158struct Parent {
159  build() {
160    Column(){
161      // @Local禁止从外部初始化,可以用@Param和@Once替代实现
162      Child({ value: 30 })
163    }
164  }
165}
166```
167
168### \@Link -> \@Param/\@Event
169
170**迁移规则**
171
172在V1中,\@Link允许父组件和子组件之间进行双向数据绑定。迁移到V2时,可以用\@Param和\@Event模拟双向同步。\@Param实现父到子的单向传递,子组件再通过\@Event回调函数触发父组件的状态更新。
173
174**示例**
175
176V1实现:
177
178```ts
179@Component
180struct Child {
181  // @Link可以双向同步数据
182  @Link val: number;
183  build() {
184    Column(){
185      Text("child: " + this.val.toString())
186      Button("+1")
187        .onClick(() => {
188          this.val++;
189        })
190    }
191  }
192}
193
194@Entry
195@Component
196struct Parent {
197  @State myVal: number = 10;
198  build() {
199    Column(){
200      Text("parent: " + this.myVal.toString())
201      Child({val: this.myVal})
202    }
203  }
204}
205```
206
207V2迁移策略:使用\@Param和\@Event。
208
209```ts
210@ComponentV2
211struct Child {
212  // @Param搭配@Event回调实现数据双向同步
213  @Param val: number  = 0;
214  @Event addOne: () => void;
215  build() {
216    Column(){
217      Text("child: " + this.val.toString())
218      Button("+1")
219        .onClick(()=> {
220          this.addOne();
221        })
222    }
223  }
224}
225
226@Entry
227@ComponentV2
228struct Parent {
229  @Local myVal: number = 10
230  build() {
231    Column() {
232      Text("parent: " + this.myVal.toString())
233      Child({ val: this.myVal, addOne: () => this.myVal++})
234    }
235  }
236}
237```
238
239### \@Prop -> \@Param
240
241**迁移规则**
242
243在V1中,\@Prop装饰器用于从父组件传递参数给子组件,这些参数在子组件中可以被直接修改。在V2中,\@Param取代了\@Prop的作用,但\@Param是只读的,子组件不能直接修改参数的值。因此,根据场景的不同,有几种迁移策略:
244
245- 简单类型:对于简单类型的参数,将\@Prop替换为\@Param。
246- 复杂类型:如果传递的是复杂对象且需要严格的单向数据绑定,需要深拷贝对象,防止子组件修改父组件的数据。
247- 子组件修改变量:如果子组件需要修改传入的参数,使用\@Once允许子组件在本地修改该变量。但需要注意,使用\@Once修饰符后,当前子组件只会被初始化一次,后续无父组件到子组件的同步能力。
248
249**示例**
250
251**简单类型**
252
253对于简单类型变量,V1的\@Prop可以直接替换为V2的\@Param。
254
255V1实现:
256
257```ts
258@Component
259struct Child {
260  @Prop value: number;
261  build() {
262    Text(this.value.toString())
263  }
264}
265
266@Entry
267@Component
268struct Parent {
269  build() {
270    Column(){
271      Child({ value: 30 })
272    }
273  }
274}
275```
276
277V2迁移策略:直接替换。
278
279```ts
280@ComponentV2
281struct Child {
282  @Param value: number = 0;
283  build() {
284    Text(this.value.toString())
285  }
286}
287
288@Entry
289@ComponentV2
290struct Parent {
291  build() {
292    Column(){
293      Child({ value: 30 })
294    }
295  }
296}
297```
298**复杂类型的单向数据传递**
299
300在V2中,传递复杂类型时,如果希望实现严格的单向数据绑定,防止子组件修改父组件的数据,需要在使用\@Param传递复杂对象时进行深拷贝以避免传递对象的引用。
301
302V1实现:
303
304```ts
305class Fruit {
306  apple: number = 5;
307  orange: number = 10;
308}
309
310@Component
311struct Child {
312  // @Prop传递Fruit类,当子类修改属性,父类不受影响
313  @Prop fruit: Fruit;
314  build() {
315    Column() {
316      Text("child apple: "+ this.fruit.apple.toString())
317      Text("child orange: "+ this.fruit.orange.toString())
318      Button("apple+1")
319        .onClick(() => {
320          this.fruit.apple++;
321        })
322      Button("orange+1")
323        .onClick(() => {
324          this.fruit.orange++;
325        })
326    }
327  }
328}
329
330@Entry
331@Component
332struct Parent {
333  @State parentFruit: Fruit = new Fruit();
334  build() {
335    Column(){
336      Text("parent apple: "+this.parentFruit.apple.toString())
337      Text("parent orange: "+this.parentFruit.orange.toString())
338      Child({ fruit: this.parentFruit })
339    }
340  }
341}
342```
343
344V2迁移策略:使用深拷贝。
345
346```ts
347@ObservedV2
348class Fruit{
349  @Trace apple: number = 5;
350  @Trace orange: number = 10;
351  // 实现深拷贝,子组件不会修改父组件的数据
352  clone(): Fruit {
353    let newFruit: Fruit = new Fruit();
354    newFruit.apple = this.apple;
355    newFruit.orange = this.orange;
356    return newFruit;
357  }
358}
359
360@ComponentV2
361struct Child {
362  @Param fruit: Fruit = new Fruit();
363  build() {
364    Column() {
365      Text("child")
366      Text(this.fruit.apple.toString())
367      Text(this.fruit.orange.toString())
368      Button("apple+1")
369        .onClick( ()=> {
370          this.fruit.apple++;
371        })
372      Button("orange+1")
373        .onClick(() => {
374          this.fruit.orange++;
375        })
376    }
377  }
378}
379
380@Entry
381@ComponentV2
382struct Parent {
383  @Local parentFruit: Fruit = new Fruit();
384  build() {
385    Column(){
386      Text("parent")
387      Text(this.parentFruit.apple.toString())
388      Text(this.parentFruit.orange.toString())
389      Child({ fruit: this.parentFruit.clone()})
390    }
391  }
392}
393```
394
395**子组件修改变量**
396
397在V1中,子组件可以修改\@Prop的变量,然而在V2中,\@Param是只读的。如果子组件需要修改传入的值,可以使用\@Param和\@Once允许子组件在本地修改。
398
399V1实现:
400
401```ts
402@Component
403struct Child {
404  // @Prop可以直接修改变量值
405  @Prop value: number;
406  build() {
407    Column(){
408      Text(this.value.toString())
409      Button("+1")
410        .onClick(()=> {
411          this.value++;
412        })
413    }
414  }
415}
416
417@Entry
418@Component
419struct Parent {
420  build() {
421    Column(){
422      Child({ value: 30 })
423    }
424  }
425}
426```
427
428V2迁移策略:使用\@Param和\@Once。
429
430```ts
431@ComponentV2
432struct Child {
433  // @Param搭配@Once使用,可以在本地修改@Param变量
434  @Param @Once value: number = 0;
435  build() {
436    Column(){
437      Text(this.value.toString())
438      Button("+1")
439        .onClick(() => {
440          this.value++;
441        })
442    }
443  }
444}
445
446@Entry
447@ComponentV2
448struct Parent {
449  build() {
450    Column(){
451      Child({ value: 30 })
452    }
453  }
454}
455```
456
457在V1中,子组件可以修改\@Prop的变量,且只会在本地更新,不会同步回父组件。父组件数据源更新时,会通知子组件更新,并覆写子组件本地\@Prop的值。
458
459V1:
460- 改变子组件`Child`的`localValue`,不会同步回父组件`Parent`。
461- 父组件更新`value`,通知子组件`Child`更新,并覆写本地子组件`localValue`的值。
462
463```ts
464@Component
465struct Child {
466  @Prop localValue: number = 0;
467
468  build() {
469    Column() {
470      Text(`${this.localValue}`).fontSize(25)
471      Button('Child +100')
472        .onClick(() => {
473          // 改变localValue不会传递给父组件Parent
474          this.localValue += 100;
475        })
476    }
477  }
478}
479
480@Entry
481@Component
482struct Parent {
483  @State value: number = 10;
484  build() {
485    Column() {
486      Button('Parent +1')
487        .onClick(() => {
488          // 改变value的值,通知子组件Child value更新
489          this.value += 1;
490        })
491      Child({ localValue: this.value })
492    }
493  }
494}
495```
496V2中,\@Param本地不可写,与\@Once搭配使用时只同步一次。若要实现子组件本地可写,且父组件后续更新仍能通知子组件,可借助\@Monitor实现。
497
498V2实现:
499- 父组件`Parent`更新通知子组件`value`的刷新,并回调\@Monitor修饰的`onValueChange`回调方法,`onValueChange`将更新后的值赋值给`localValue`。
500- 子组件`Child`改变`localValue`的值,不会同步给父组件`Parent`。
501- 父组件`Parent`中再次改变`value`,将会继续通知给子组件,并覆写子组件本地`localValue`的值。
502
503```ts
504@ComponentV2
505struct Child {
506  @Local localValue: number = 0;
507  @Param value: number = 0;
508  @Monitor('value')
509  onValueChange(mon: IMonitor) {
510    console.info(`value has been changed from ${mon.value()?.before} to ${mon.value()?.now}`);
511    // 父组件value变化时,通知子组件value更新,回调Monitor函数,将更新的值覆写给本地的localValue
512    this.localValue = this.value;
513  }
514
515  build() {
516    Column() {
517      Text(`${this.localValue}`).fontSize(25)
518      Button('Child +100')
519        .onClick(() => {
520          // 改变localValue不会传递给父组件Parent
521          this.localValue += 100;
522        })
523    }
524  }
525}
526
527@Entry
528@ComponentV2
529struct Parent {
530  @Local value: number = 10;
531  build() {
532    Column() {
533      Button('Parent +1')
534        .onClick(() => {
535          // 改变value的值,通知子组件Child value更新
536          this.value += 1;
537        })
538      Child({ value: this.value })
539    }
540  }
541}
542```
543
544### \@Provide/\@Consume -> \@Provider/\@Consumer
545**迁移规则**
546
547V1的\@Provide和\@Consume与V2的\@Provider和\@Consumer定位和作用类似,基本可以实现丝滑替换,但存在以下细微差异,开发者可根据自己代码实现情况参考是否需要调整:
548在V1中,\@Provide和\@Consume用于父子组件之间的数据共享,可以通过alias(别名)或属性名匹配,同时\@Consume依赖父组件的\@Provide,API version 20以前不允许本地初始化。V2中,\@Provider和\@Consumer增强了这些特性,使数据共享更加灵活。根据不同的场景,有以下迁移策略:
549
550- V1中\@Provide和\@Consume在没有指定alias的情况下,可以直接使用。V2中\@Provider和\@Consumer是标准装饰器,且参数可选,所以不管有无指定alias后面需要必须跟随“()”。
551- alias和属性名匹配规则:V1中,\@Provide和\@Consume可以通过alias或属性名匹配;V2中,alias是唯一的匹配key,指定alias后只能通过alias匹配。
552- 本地初始化支持:API version 20以前,\@Consume不允许本地初始化,必须依赖父组件;从API version 20开始,\@Consume支持本地初始化,当找不到对应的\@Provide时使用本地默认值,详见[\@Consume装饰的变量支持设置默认值](./arkts-provide-and-consume.md#consume装饰的变量支持设置默认值);V2中,\@Consumer支持本地初始化,当找不到对应的\@Provider时使用本地默认值。
553- 从父组件初始化:V1中,\@Provide可以直接从父组件初始化;V2中,\@Provider不支持外部初始化,需用\@Param和@Once接受初始值并赋给 \@Provider。
554- 重载支持:V1中,\@Provide默认不支持重载,需设置 allowOverride;V2中,\@Provider默认支持重载,\@Consumer会向上查找最近的\@Provider。
555
556**示例**
557
558**alias和属性名匹配规则**
559
560在V1中,\@Provide和\@Consume的匹配既可以通过alias,也可以通过属性名。在V2中,alias成为唯一的key,如果在\@Consumer中制定了alias,只能通过alias而非属性名进行匹配。
561
562V1实现:
563
564```ts
565@Component
566struct Child {
567  // alias和属性名都为key,alias和属性名都可以匹配
568  @Consume('text') childMessage: string;
569  @Consume message: string;
570  build(){
571    Column(){
572      Text(this.childMessage)
573      Text(this.message) // Text是Hello World
574    }
575  }
576}
577
578@Entry
579@Component
580struct Parent {
581  @Provide('text') message: string = "Hello World";
582  build(){
583    Column(){
584      Child()
585    }
586  }
587}
588```
589
590V2迁移策略:确保alias一致,没有指定alias的情况下,依赖属性名进行匹配。
591
592```ts
593@ComponentV2
594struct Child {
595  // alias是唯一匹配的key,有alias情况下无法通过属性名匹配
596  @Consumer('text') childMessage: string = "default";
597  @Consumer() message: string = "default";
598  build(){
599    Column(){
600      Text(this.childMessage)
601      Text(this.message) // Text是default
602    }
603  }
604}
605
606@Entry
607@ComponentV2
608struct Parent {
609  @Provider('text') message: string = "Hello World";
610  build(){
611    Column(){
612      Child()
613    }
614  }
615}
616```
617
618**V1的\@Consume不支持本地初始化,V2支持**
619
620V1中,API version 20之前,\@Consume不允许本地初始化变量,必须依赖父组件的\@Provide,否则会抛出异常。迁移到V2后,\@Consumer允许本地初始化,当找不到对应的\@Provider,会使用本地默认值。
621
622V1实现:
623
624```ts
625@Component
626struct Child {
627  // @Consume禁止本地初始化,当找不到对应的@Provide时抛出异常
628  @Consume message: string;
629  build(){
630    Text(this.message)
631  }
632}
633
634@Entry
635@Component
636struct Parent {
637  @Provide message: string = "Hello World";
638  build(){
639    Column(){
640      Child()
641    }
642  }
643}
644```
645
646V2迁移策略:\@Consumer可以本地初始化。
647
648```ts
649@ComponentV2
650struct Child {
651  // @Consumer允许本地初始化,当找不到@Provider的时候使用本地默认值
652  @Consumer() message: string = "Hello World";
653  build(){
654    Text(this.message)
655  }
656}
657
658@Entry
659@ComponentV2
660struct Parent {
661  build(){
662    Column(){
663      Child()
664    }
665  }
666}
667```
668
669**V1的\@Provide可以从父组件初始化,V2不支持**
670
671在V1中,\@Provide允许从父组件初始化,可以直接通过组件参数传递初始值。在V2中,\@Provider禁止从外部初始化。为实现相同功能,可以在子组件中使用\@Param \@Once接受初始值,然后将其赋值给\@Provider变量。
672
673V1实现:
674
675```ts
676@Entry
677@Component
678struct Parent {
679  @State parentValue: number = 42;
680  build() {
681    Column() {
682      // @Provide可以从父组件初始化
683      Child({ childValue: this.parentValue })
684    }
685  }
686}
687
688@Component
689struct Child {
690  @Provide childValue: number = 0;
691  build(){
692    Column(){
693      Text(this.childValue.toString())
694    }
695  }
696}
697```
698
699V2迁移策略:使用\@Param接受初始值,再赋值给\@Provider。
700
701```ts
702@Entry
703@ComponentV2
704struct Parent {
705  @Local parentValue: number = 42;
706  build() {
707    Column() {
708      // @Provider禁止从父组件初始化,替代方案为先用@Param接受,再赋值给@Provider
709      Child({ initialValue: this.parentValue })
710    }
711  }
712}
713
714@ComponentV2
715struct Child {
716  @Param @Once initialValue: number = 0;
717  @Provider() childValue: number = this.initialValue;
718  build() {
719    Column(){
720      Text(this.childValue.toString())
721    }
722  }
723}
724```
725
726**V1的\@Provide默认不支持重载,V2默认支持**
727
728在V1中,\@Provide默认不支持重载,无法覆盖上层组件的同名\@Provide。若需支持重载,必须设置allowOverride。在V2中,\@Provider默认支持重载,\@Consumer会向上查找最近的\@Provider,无需额外设置。
729
730V1实现:
731
732```ts
733@Entry
734@Component
735struct GrandParent {
736  @Provide("reviewVotes") reviewVotes: number = 40;
737  build() {
738    Column(){
739      Parent()
740    }
741  }
742}
743
744@Component
745struct Parent {
746  // @Provide默认不支持重载,支持重载需设置allowOverride函数
747  @Provide({ allowOverride: "reviewVotes" }) reviewVotes: number = 20;
748  build() {
749    Child()
750  }
751}
752
753@Component
754struct Child {
755  @Consume("reviewVotes") reviewVotes: number;
756  build() {
757    Text(this.reviewVotes.toString()) // Text显示20
758  }
759}
760```
761
762V2迁移策略:去掉allowOverride。
763
764```ts
765@Entry
766@ComponentV2
767struct GrandParent {
768  @Provider("reviewVotes") reviewVotes: number = 40;
769  build() {
770    Column(){
771      Parent()
772    }
773  }
774}
775
776@ComponentV2
777struct Parent {
778  // @Provider默认支持重载,@Consumer向上查找最近的@Provider
779  @Provider() reviewVotes: number = 20;
780  build() {
781    Child()
782  }
783}
784
785@ComponentV2
786struct Child {
787  @Consumer() reviewVotes: number = 0;
788  build() {
789    Text(this.reviewVotes.toString()) // Text显示20
790  }
791}
792```
793
794### \@Watch -> \@Monitor
795**迁移规则**
796
797在V1中,\@Watch用于监听状态变量的变化,并在变量变化时触发指定回调函数。在V2中,\@Monitor替代了\@Watch,可以更灵活地监听变量的变化,并获取变量变化前后的值。具体的迁移策略如下:
798
799- 单变量监听:对于简单的场景,可以直接用\@Monitor替换\@Watch,效果一致。
800- 多变量监听:V1的\@Watch无法获取变化前的值。在V2中,\@Monitor支持同时监听多个变量,并可以访问变量变化前后的状态。
801**示例**
802
803**单变量监听**
804
805对于简单案例,V1的\@Watch可以直接替换为V2的\@Monitor。
806
807V1实现:
808
809```ts
810@Entry
811@Component
812struct watchExample {
813  @State @Watch('onAppleChange') apple: number = 0;
814  onAppleChange(): void {
815    console.log("apple count changed to "+this.apple);
816  }
817
818  build() {
819    Column(){
820      Text(`apple count: ${this.apple}`)
821      Button("add apple")
822        .onClick(() => {
823          this.apple++;
824        })
825    }
826  }
827}
828```
829
830V2迁移策略:直接替换。
831
832```ts
833@Entry
834@ComponentV2
835struct monitorExample {
836  @Local apple: number = 0;
837  @Monitor('apple')
838  onFruitChange(monitor: IMonitor) {
839    console.log(`apple changed from ${monitor.value()?.before} to ${monitor.value()?.now}`);
840  }
841
842  build() {
843    Column(){
844      Text(`apple count: ${this.apple}`)
845      Button("add apple")
846        .onClick(()=> {
847          this.apple++;
848        })
849    }
850  }
851}
852```
853
854**多变量监听**
855
856在V1中,每个\@Watch回调函数只能监听一个变量,且无法获取变化前的值。迁移到V2后,可以使用一个\@Monitor同时监听多个变量,并获取监听变量变化前后的值。
857
858V1实现:
859
860```ts
861@Entry
862@Component
863struct watchExample {
864  @State @Watch('onAppleChange') apple: number = 0;
865  @State @Watch('onOrangeChange') orange: number = 0;
866  // @Watch 回调,只能监听单个变量,不能获取变化前的值
867  onAppleChange(): void {
868    console.log("apple count changed to "+this.apple);
869  }
870  onOrangeChange(): void {
871    console.log("orange count changed to "+this.orange);
872  }
873
874  build() {
875    Column(){
876      Text(`apple count: ${this.apple}`)
877      Text(`orange count: ${this.orange}`)
878      Button("add apple")
879        .onClick(() => {
880          this.apple++;
881        })
882      Button("add orange")
883        .onClick(() => {
884          this.orange++;
885        })
886    }
887  }
888}
889```
890
891V2迁移策略:同时监听多个变量,以及获取变化前的值。
892
893```ts
894@Entry
895@ComponentV2
896struct monitorExample {
897  @Local apple: number = 0;
898  @Local orange: number = 0;
899
900  // @Monitor回调,支持监听多个变量,可以获取变化前的值
901  @Monitor('apple','orange')
902  onFruitChange(monitor: IMonitor) {
903    monitor.dirty.forEach((name: string) => {
904      console.log(`${name} changed from ${monitor.value(name)?.before} to ${monitor.value(name)?.now}`);
905    });
906  }
907
908  build() {
909    Column() {
910      Text(`apple count: ${this.apple}`)
911      Text(`orange count: ${this.orange}`)
912      Button("add apple")
913        .onClick(() => {
914          this.apple++;
915        })
916      Button("add orange")
917        .onClick(() => {
918          this.orange++;
919        })
920    }
921  }
922}
923```
924### \@Computed
925**迁移规则**
926
927V1中并没有提供计算属性的概念,所以对于UI中的冗余计算,并没有办法可以减少重复计算。V2针对该场景,提供了\@Computed装饰器,可以帮助开发者减少重复计算。
928
929V1:
930
931在下面的示例中,每次改变`lastName`都会触发Text组件的刷新,每次Text组件的刷新,都需要重复计算`this.lastName + ' ' + this.firstName`。
932
933```ts
934@Entry
935@Component
936struct Index {
937  @State firstName: string = 'Li';
938  @State lastName: string = 'Hua';
939
940  build() {
941    Column() {
942      Text(this.lastName + ' ' + this.firstName)
943      Text(this.lastName + ' ' + this.firstName)
944      Button('changed lastName').onClick(() => {
945        this.lastName += 'a';
946      })
947
948    }
949  }
950}
951```
952
953V2:
954
955使用V2中的\@Computed,每次改变`lastName`仅会触发一次计算。
956
957```ts
958@Entry
959@ComponentV2
960struct Index {
961  @Local firstName: string = 'Li';
962  @Local lastName: string = 'Hua';
963
964  @Computed
965  get fullName() {
966    return this.firstName + ' ' + this.lastName;
967  }
968
969  build() {
970    Column() {
971      Text(this.fullName)
972      Text(this.fullName)
973      Button('changed lastName').onClick(() => {
974        this.lastName += 'a';
975      })
976    }
977  }
978}
979```