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```