1# \@Provide装饰器和\@Consume装饰器:与后代组件双向同步 2 3 4\@Provide和\@Consume,应用于与后代组件的双向数据同步,应用于状态数据在多个层级之间传递的场景。不同于上文提到的父子组件之间通过命名参数机制传递,\@Provide和\@Consume摆脱参数传递机制的束缚,实现跨层级传递。 5 6 7其中\@Provide装饰的变量是在祖先组件中,可以理解为被“提供”给后代的状态变量。\@Consume装饰的变量是在后代组件中,去“消费(绑定)”祖先组件提供的变量。 8 9 10> **说明:** 11> 12> 从API version 9开始,这两个装饰器支持在ArkTS卡片中使用。 13> 14> 从API version 11开始,这两个装饰器支持在原子化服务中使用。 15 16## 概述 17 18\@Provide/\@Consume装饰的状态变量有以下特性: 19 20- \@Provide装饰的状态变量自动对其所有后代组件可用,即该变量被“provide”给他的后代组件。由此可见,\@Provide的方便之处在于,开发者不需要多次在组件之间传递变量。 21 22- 后代通过使用\@Consume去获取\@Provide提供的变量,建立在\@Provide和\@Consume之间的双向数据同步,与\@State/\@Link不同的是,前者可以在多层级的父子组件之间传递。 23 24- \@Provide和\@Consume可以通过相同的变量名或者相同的变量别名绑定,建议类型相同,否则会发生类型隐式转换,从而导致应用行为异常。 25 26 27```ts 28// 通过相同的变量名绑定 29@Provide a: number = 0; 30@Consume a: number; 31 32// 通过相同的变量别名绑定 33@Provide('a') b: number = 0; 34@Consume('a') c: number; 35``` 36 37 38\@Provide和\@Consume通过相同的变量名或者相同的变量别名绑定时,\@Provide装饰的变量和\@Consume装饰的变量是一对多的关系。不允许在同一个自定义组件内,包括其子组件中声明多个同名或者同别名的\@Provide装饰的变量,@Provide的属性名或别名需要唯一且确定,如果声明多个同名或者同别名的@Provide装饰的变量,会发生运行时报错。 39 40 41## 装饰器说明 42 43\@State的规则同样适用于\@Provide,差异为\@Provide还作为多层后代的同步源。 44 45| \@Provide变量装饰器 | 说明 | 46| -------------- | ---------------------------------------- | 47| 装饰器参数 | 别名:常量字符串,可选。<br/>如果指定了别名,则通过别名来绑定变量;如果未指定别名,则通过变量名绑定变量。 | 48| 同步类型 | 双向同步。<br/>从\@Provide变量到所有\@Consume变量以及相反的方向的数据同步。双向同步的操作与\@State和\@Link的组合相同。 | 49| 允许装饰的变量类型 | Object、class、string、number、boolean、enum类型,以及这些类型的数组。<br/>支持Date类型。<br/>API11及以上支持Map、Set类型。<br/>支持ArkUI框架定义的联合类型Length、ResourceStr、ResourceColor类型。<br/>必须指定类型。<br/>\@Provide变量的\@Consume变量的类型必须相同。<br/>支持类型的场景请参考[观察变化](#观察变化)。<br/>不支持any。<br/>API11及以上支持上述支持类型的联合类型,比如string \| number, string \| undefined 或者 ClassA \| null,示例见[@Provide_and_Consume支持联合类型实例](#provide_and_consume支持联合类型实例)。 <br/>**注意**<br/>当使用undefined和null的时候,建议显式指定类型,遵循TypeScript类型校验,比如:`@Provide a : string \| undefined = undefined`是推荐的,不推荐`@Provide a: string = undefined`。 50| 被装饰变量的初始值 | 必须指定。 | 51| 支持allowOverride参数 | 允许重写,只要声明了allowOverride,则别名和属性名都可以被Override。示例见\@Provide支持allowOverride参数。 | 52 53| \@Consume变量装饰器 | 说明 | 54| -------------- | ---------------------------------------- | 55| 装饰器参数 | 别名:常量字符串,可选。<br/>如果提供了别名,则必须有\@Provide的变量和其有相同的别名才可以匹配成功;否则,则需要变量名相同才能匹配成功。 | 56| 同步类型 | 双向:从\@Provide变量(具体请参见\@Provide)到所有\@Consume变量,以及相反的方向。双向同步操作与\@State和\@Link的组合相同。 | 57| 允许装饰的变量类型 | Object、class、string、number、boolean、enum类型,以及这些类型的数组。<br/>支持Date类型。<br/>支持ArkUI框架定义的联合类型Length、ResourceStr、ResourceColor类型。必须指定类型。<br/>\@Provide变量和\@Consume变量的类型必须相同。<br/>\@Consume装饰的变量,在其父组件或者祖先组件上,必须有对应的属性和别名的\@Provide装饰的变量。<br/>支持类型的场景请参考[观察变化](#观察变化)。<br/>不支持any。<br/>API11及以上支持上述支持类型的联合类型,比如string \| number, string \| undefined 或者 ClassA \| null,示例见[@Provide_and_Consume支持联合类型实例](#provide_and_consume支持联合类型实例)。 <br/>**注意**<br/>当使用undefined和null的时候,建议显式指定类型,遵循TypeScript类型校验,比如:`@Consume a : string \| undefined`。 58| 被装饰变量的初始值 | 无,禁止本地初始化。 | 59 60 61## 变量的传递/访问规则说明 62 63 64| \@Provide传递/访问 | 说明 | 65| -------------- | ---------------------------------------- | 66| 从父组件初始化和更新 | 可选,允许父组件中常规变量(常规变量对@Provide赋值,只是数值的初始化,常规变量的变化不会触发UI刷新,只有状态变量才能触发UI刷新)、[\@State](./arkts-state.md)、[\@Link](./arkts-link.md)、[\@Prop](./arkts-prop.md)、\@Provide、\@Consume、[\@ObjectLink](./arkts-observed-and-objectlink.md)、[\@StorageLink](./arkts-appstorage.md#storagelink)、[\@StorageProp](./arkts-appstorage.md#storageprop)、[\@LocalStorageLink](./arkts-localstorage.md#localstoragelink)和[\@LocalStorageProp](./arkts-localstorage.md#localstorageprop)装饰的变量装饰变量初始化子组件\@Provide。 | 67| 用于初始化子组件 | 允许,可用于初始化\@State、\@Link、\@Prop、\@Provide。 | 68| 和父组件同步 | 否。 | 69| 和后代组件同步 | 和\@Consume双向同步。 | 70| 是否支持组件外访问 | 私有,仅可以在所属组件内访问。 | 71 72 73 **图1** \@Provide初始化规则图示 74 75 76 77 78 79| \@Consume传递/访问 | 说明 | 80| -------------- | ---------------------------------------- | 81| 从父组件初始化和更新 | 禁止。通过相同的变量名和alias(别名)从\@Provide初始化。 | 82| 用于初始化子组件 | 允许,可用于初始化\@State、\@Link、\@Prop、\@Provide。 | 83| 和祖先组件同步 | 和\@Provide双向同步。 | 84| 是否支持组件外访问 | 私有,仅可以在所属组件内访问 | 85 86 87 **图2** \@Consume初始化规则图示 88 89 90 91 92 93## 观察变化和行为表现 94 95 96### 观察变化 97 98- 当装饰的数据类型为boolean、string、number类型时,可以观察到数值的变化。 99 100- 当装饰的数据类型为class或者Object的时候,可以观察到赋值和属性赋值的变化(属性为Object.keys(observedObject)返回的所有属性)。 101 102- 当装饰的对象是array的时候,可以观察到数组的添加、删除、更新数组单元。 103 104- 当装饰的对象是Date时,可以观察到Date整体的赋值,同时可通过调用Date的接口`setFullYear`, `setMonth`, `setDate`, `setHours`, `setMinutes`, `setSeconds`, `setMilliseconds`, `setTime`, `setUTCFullYear`, `setUTCMonth`, `setUTCDate`, `setUTCHours`, `setUTCMinutes`, `setUTCSeconds`, `setUTCMilliseconds` 更新Date的属性。 105 106```ts 107@Component 108struct CompD { 109 @Consume selectedDate: Date; 110 111 build() { 112 Column() { 113 Button(`child increase the day by 1`) 114 .onClick(() => { 115 this.selectedDate.setDate(this.selectedDate.getDate() + 1) 116 }) 117 Button('child update the new date') 118 .margin(10) 119 .onClick(() => { 120 this.selectedDate = new Date('2023-09-09') 121 }) 122 DatePicker({ 123 start: new Date('1970-1-1'), 124 end: new Date('2100-1-1'), 125 selected: this.selectedDate 126 }) 127 } 128 } 129} 130 131@Entry 132@Component 133struct CompA { 134 @Provide selectedDate: Date = new Date('2021-08-08') 135 136 build() { 137 Column() { 138 Button('parent increase the day by 1') 139 .margin(10) 140 .onClick(() => { 141 this.selectedDate.setDate(this.selectedDate.getDate() + 1) 142 }) 143 Button('parent update the new date') 144 .margin(10) 145 .onClick(() => { 146 this.selectedDate = new Date('2023-07-07') 147 }) 148 DatePicker({ 149 start: new Date('1970-1-1'), 150 end: new Date('2100-1-1'), 151 selected: this.selectedDate 152 }) 153 CompD() 154 } 155 } 156} 157``` 158 159- 当装饰的变量是Map时,可以观察到Map整体的赋值,同时可通过调用Map的接口`set`, `clear`, `delete` 更新Map的值。详见[装饰Map类型变量](#装饰map类型变量)。 160 161- 当装饰的变量是Set时,可以观察到Set整体的赋值,同时可通过调用Set的接口`add`, `clear`, `delete` 更新Set的值。详见[装饰Set类型变量](#装饰set类型变量)。 162 163### 框架行为 164 1651. 初始渲染: 166 1. \@Provide装饰的变量会以map的形式,传递给当前\@Provide所属组件的所有子组件; 167 2. 子组件中如果使用\@Consume变量,则会在map中查找是否有该变量名/alias(别名)对应的\@Provide的变量,如果查找不到,框架会抛出JS ERROR; 168 3. 在初始化\@Consume变量时,和\@State/\@Link的流程类似,\@Consume变量会在map中查找到对应的\@Provide变量进行保存,并把自己注册给\@Provide。 169 1702. 当\@Provide装饰的数据变化时: 171 1. 通过初始渲染的步骤可知,子组件\@Consume已把自己注册给父组件。父组件\@Provide变量变更后,会遍历更新所有依赖它的系统组件(elementid)和状态变量(\@Consume); 172 2. 通知\@Consume更新后,子组件所有依赖\@Consume的系统组件(elementId)都会被通知更新。以此实现\@Provide对\@Consume状态数据同步。 173 1743. 当\@Consume装饰的数据变化时: 175 176 通过初始渲染的步骤可知,子组件\@Consume持有\@Provide的实例。在\@Consume更新后调用\@Provide的更新方法,将更新的数值同步回\@Provide,以此实现\@Consume向\@Provide的同步更新。 177 178 179 180 181## 使用场景 182 183在下面的示例是与后代组件双向同步状态\@Provide和\@Consume场景。当分别点击CompA和CompD组件内Button时,reviewVotes 的更改会双向同步在CompA和CompD中。 184 185 186 187```ts 188@Component 189struct CompD { 190 // @Consume装饰的变量通过相同的属性名绑定其祖先组件CompA内的@Provide装饰的变量 191 @Consume reviewVotes: number; 192 193 build() { 194 Column() { 195 Text(`reviewVotes(${this.reviewVotes})`) 196 Button(`reviewVotes(${this.reviewVotes}), give +1`) 197 .onClick(() => this.reviewVotes += 1) 198 } 199 .width('50%') 200 } 201} 202 203@Component 204struct CompC { 205 build() { 206 Row({ space: 5 }) { 207 CompD() 208 CompD() 209 } 210 } 211} 212 213@Component 214struct CompB { 215 build() { 216 CompC() 217 } 218} 219 220@Entry 221@Component 222struct CompA { 223 // @Provide装饰的变量reviewVotes由入口组件CompA提供其后代组件 224 @Provide reviewVotes: number = 0; 225 226 build() { 227 Column() { 228 Button(`reviewVotes(${this.reviewVotes}), give +1`) 229 .onClick(() => this.reviewVotes += 1) 230 CompB() 231 } 232 } 233} 234``` 235 236### 装饰Map类型变量 237 238> **说明:** 239> 240> 从API version 11开始,\@Provide,\@Consume支持Map类型。 241 242在下面的示例中,message类型为Map<number, string>,点击Button改变message的值,视图会随之刷新。 243 244```ts 245@Component 246struct Child { 247 @Consume message: Map<number, string> 248 249 build() { 250 Column() { 251 ForEach(Array.from(this.message.entries()), (item: [number, string]) => { 252 Text(`${item[0]}`).fontSize(30) 253 Text(`${item[1]}`).fontSize(30) 254 Divider() 255 }) 256 Button('Consume init map').onClick(() => { 257 this.message = new Map([[0, "a"], [1, "b"], [3, "c"]]) 258 }) 259 Button('Consume set new one').onClick(() => { 260 this.message.set(4, "d") 261 }) 262 Button('Consume clear').onClick(() => { 263 this.message.clear() 264 }) 265 Button('Consume replace the first item').onClick(() => { 266 this.message.set(0, "aa") 267 }) 268 Button('Consume delete the first item').onClick(() => { 269 this.message.delete(0) 270 }) 271 } 272 } 273} 274 275 276@Entry 277@Component 278struct MapSample { 279 @Provide message: Map<number, string> = new Map([[0, "a"], [1, "b"], [3, "c"]]) 280 281 build() { 282 Row() { 283 Column() { 284 Button('Provide init map').onClick(() => { 285 this.message = new Map([[0, "a"], [1, "b"], [3, "c"], [4, "d"]]) 286 }) 287 Child() 288 } 289 .width('100%') 290 } 291 .height('100%') 292 } 293} 294``` 295 296### 装饰Set类型变量 297 298> **说明:** 299> 300> 从API version 11开始,\@Provide,\@Consume支持Set类型。 301 302在下面的示例中,message类型为Set\<number\>,点击Button改变message的值,视图会随之刷新。 303 304```ts 305@Component 306struct Child { 307 @Consume message: Set<number> 308 309 build() { 310 Column() { 311 ForEach(Array.from(this.message.entries()), (item: [number, string]) => { 312 Text(`${item[0]}`).fontSize(30) 313 Divider() 314 }) 315 Button('Consume init set').onClick(() => { 316 this.message = new Set([0, 1, 2, 3, 4]) 317 }) 318 Button('Consume set new one').onClick(() => { 319 this.message.add(5) 320 }) 321 Button('Consume clear').onClick(() => { 322 this.message.clear() 323 }) 324 Button('Consume delete the first one').onClick(() => { 325 this.message.delete(0) 326 }) 327 } 328 .width('100%') 329 } 330} 331 332 333@Entry 334@Component 335struct SetSample { 336 @Provide message: Set<number> = new Set([0, 1, 2, 3, 4]) 337 338 build() { 339 Row() { 340 Column() { 341 Button('Provide init set').onClick(() => { 342 this.message = new Set([0, 1, 2, 3, 4, 5]) 343 }) 344 Child() 345 } 346 .width('100%') 347 } 348 .height('100%') 349 } 350} 351``` 352 353### Provide_and_Consume支持联合类型实例 354 355@Provide和@Consume支持联合类型和undefined和null,在下面的示例中,count类型为string | undefined,点击父组件Parent中的Button改变count的属性或者类型,Child中也会对应刷新。 356 357```ts 358@Component 359struct Child { 360 // @Consume装饰的变量通过相同的属性名绑定其祖先组件Ancestors内的@Provide装饰的变量 361 @Consume count: string | undefined; 362 363 build() { 364 Column() { 365 Text(`count(${this.count})`) 366 Button(`count(${this.count}), Child`) 367 .onClick(() => this.count = 'Ancestors') 368 } 369 .width('50%') 370 } 371} 372 373@Component 374struct Parent { 375 build() { 376 Row({ space: 5 }) { 377 Child() 378 } 379 } 380} 381 382@Entry 383@Component 384struct Ancestors { 385 // @Provide装饰的联合类型count由入口组件Ancestors提供其后代组件 386 @Provide count: string | undefined = 'Child'; 387 388 build() { 389 Column() { 390 Button(`count(${this.count}), Child`) 391 .onClick(() => this.count = undefined) 392 Parent() 393 } 394 } 395} 396``` 397 398### \@Provide支持allowOverride参数 399 400allowOverride:\@Provide重写选项。 401 402> **说明:** 403> 404> 从API version 11开始使用。 405 406| 名称 | 类型 | 必填 | 说明 | 407| ------ | ------ | ---- | ------------------------------------------------------------ | 408| allowOverride | string | 否 | 是否允许@Provide重写。允许在同一组件树下通过allowOverride重写同名的@Provide。如果开发者未写allowOverride,定义同名的@Provide,运行时会报错。 | 409 410```ts 411@Component 412struct MyComponent { 413 @Provide({allowOverride : "reviewVotes"}) reviewVotes: number = 10; 414} 415``` 416 417```ts 418@Component 419struct GrandSon { 420 // @Consume装饰的变量通过相同的属性名绑定其祖先内的@Provide装饰的变量 421 @Consume("reviewVotes") reviewVotes: number; 422 423 build() { 424 Column() { 425 Text(`reviewVotes(${this.reviewVotes})`) // Text显示10 426 Button(`reviewVotes(${this.reviewVotes}), give +1`) 427 .onClick(() => this.reviewVotes += 1) 428 } 429 .width('50%') 430 } 431} 432 433@Component 434struct Child { 435 @Provide({ allowOverride: "reviewVotes" }) reviewVotes: number = 10; 436 437 build() { 438 Row({ space: 5 }) { 439 GrandSon() 440 } 441 } 442} 443 444@Component 445struct Parent { 446 @Provide({ allowOverride: "reviewVotes" }) reviewVotes: number = 20; 447 448 build() { 449 Child() 450 } 451} 452 453@Entry 454@Component 455struct GrandParent { 456 @Provide("reviewVotes") reviewVotes: number = 40; 457 458 build() { 459 Column() { 460 Button(`reviewVotes(${this.reviewVotes}), give +1`) 461 .onClick(() => this.reviewVotes += 1) 462 Parent() 463 } 464 } 465} 466``` 467 468在上面的示例中: 469- GrandParent声明了@Provide("reviewVotes") reviewVotes: number = 40 470- Parent是GrandParent的子组件,声明@Provide为allowOverride,重写父组件GrandParent的@Provide("reviewVotes") reviewVotes: number = 40。如果不设置allowOverride,则会抛出运行时报错,提示@Provide重复定义。Child同理。 471- GrandSon在初始化@Consume的时候,@Consume装饰的变量通过相同的属性名绑定其最近的祖先的@Provide装饰的变量。 472- GrandSon查找到相同属性名的@Provide在祖先Child中,所以@Consume("reviewVotes") reviewVotes: number初始化数值为10。如果Child中没有定义与@Consume同名的@Provide,则继续向上寻找Parent中的同名@Provide值为20,以此类推。 473- 如果查找到根节点还没有找到key对应的@Provide,则会报初始化@Consume找不到@Provide的报错。 474 475 476## 常见问题 477 478### \@BuilderParam尾随闭包情况下\@Provide未定义错误 479 480在此场景下,CustomWidget执行this.builder()创建子组件CustomWidgetChild时,this指向的是HomePage。因此找不到CustomWidget的\@Provide变量,所以下面示例会报找不到\@Provide错误,和\@BuilderParam连用的时候要谨慎this的指向。 481 482错误示例: 483 484```ts 485class Tmp { 486 a: string = '' 487} 488 489@Entry 490@Component 491struct HomePage { 492 @Builder 493 builder2($$: Tmp) { 494 Text(`${$$.a}测试`) 495 } 496 497 build() { 498 Column() { 499 CustomWidget() { 500 CustomWidgetChild({ builder: this.builder2 }) 501 } 502 } 503 } 504} 505 506@Component 507struct CustomWidget { 508 @Provide('a') a: string = 'abc'; 509 @BuilderParam 510 builder: () => void; 511 512 build() { 513 Column() { 514 Button('你好').onClick(() => { 515 if (this.a == 'ddd') { 516 this.a = 'abc'; 517 } 518 else { 519 this.a = 'ddd'; 520 } 521 522 }) 523 this.builder() 524 } 525 } 526} 527 528@Component 529struct CustomWidgetChild { 530 @Consume('a') a: string; 531 @BuilderParam 532 builder: ($$: Tmp) => void; 533 534 build() { 535 Column() { 536 this.builder({ a: this.a }) 537 } 538 } 539} 540``` 541 542正确示例: 543 544```ts 545class Tmp { 546 name: string = '' 547} 548 549@Entry 550@Component 551struct HomePage { 552 @Provide('name') name: string = 'abc'; 553 554 @Builder 555 builder2($$: Tmp) { 556 Text(`${$$.name}测试`) 557 } 558 559 build() { 560 Column() { 561 Button('你好').onClick(() => { 562 if (this.name == 'ddd') { 563 this.name = 'abc'; 564 } else { 565 this.name = 'ddd'; 566 } 567 }) 568 CustomWidget() { 569 CustomWidgetChild({ builder: this.builder2 }) 570 } 571 } 572 } 573} 574 575@Component 576struct CustomWidget { 577 @BuilderParam 578 builder: () => void; 579 580 build() { 581 this.builder() 582 } 583} 584 585@Component 586struct CustomWidgetChild { 587 @Consume('name') name: string; 588 @BuilderParam 589 builder: ($$: Tmp) => void; 590 591 build() { 592 Column() { 593 this.builder({ name: this.name }) 594 } 595 } 596} 597``` 598 599### 使用a.b(this.object)形式调用,不会触发UI刷新 600 601在build方法内,当@Provide与@Consume装饰的变量是Object类型、且通过a.b(this.object)形式调用时,b方法内传入的是this.object的原生对象,修改其属性,无法触发UI刷新。如下例中,通过静态方法或者使用this调用组件内部方法,修改组件中的this.dog.age与this.dog.name时,UI不会刷新。 602 603【反例】 604 605```ts 606class Animal { 607 name:string; 608 type:string; 609 age: number; 610 611 constructor(name:string, type:string, age:number) { 612 this.name = name; 613 this.type = type; 614 this.age = age; 615 } 616 617 static changeName1(animal:Animal) { 618 animal.name = 'Black'; 619 } 620 static changeAge1(animal:Animal) { 621 animal.age += 1; 622 } 623} 624 625@Entry 626@Component 627struct Demo1 { 628 @Provide dog:Animal = new Animal('WangCai', 'dog', 2); 629 630 changeAge2(animal:Animal) { 631 animal.age += 2; 632 } 633 634 build() { 635 Column({ space:10 }) { 636 Text(`Demo1: This is a ${this.dog.age}-year-old ${this.dog.type} named ${this.dog.name}.`) 637 .fontColor(Color.Red) 638 .fontSize(30) 639 Button('changeAge1') 640 .onClick(()=>{ 641 // 通过静态方法调用,无法触发UI刷新 642 Animal.changeAge1(this.dog); 643 }) 644 Button('changeAge2') 645 .onClick(()=>{ 646 // 使用this通过自定义组件内部方法调用,无法触发UI刷新 647 this.changeAge2(this.dog); 648 }) 649 Demo2() 650 } 651 } 652} 653 654@Component 655struct Demo2 { 656 657 build() { 658 Column({ space:10 }) { 659 Text(`Demo2.`) 660 .fontColor(Color.Blue) 661 .fontSize(30) 662 Demo3() 663 } 664 } 665} 666 667@Component 668struct Demo3 { 669 @Consume dog:Animal; 670 671 changeName2(animal:Animal) { 672 animal.name = 'White'; 673 } 674 675 build() { 676 Column({ space:10 }) { 677 Text(`Demo3: This is a ${this.dog.age}-year-old ${this.dog.type} named ${this.dog.name}.`) 678 .fontColor(Color.Yellow) 679 .fontSize(30) 680 Button('changeName1') 681 .onClick(()=>{ 682 // 通过静态方法调用,无法触发UI刷新 683 Animal.changeName1(this.dog); 684 }) 685 Button('changeName2') 686 .onClick(()=>{ 687 // 使用this通过自定义组件内部方法调用,无法触发UI刷新 688 this.changeName2(this.dog); 689 }) 690 } 691 } 692} 693``` 694 695可以通过如下先赋值、再调用新赋值的变量的方式为this.dog加上Proxy代理,实现UI刷新。 696 697【正例】 698 699```ts 700class Animal { 701 name:string; 702 type:string; 703 age: number; 704 705 constructor(name:string, type:string, age:number) { 706 this.name = name; 707 this.type = type; 708 this.age = age; 709 } 710 711 static changeName1(animal:Animal) { 712 animal.name = 'Black'; 713 } 714 static changeAge1(animal:Animal) { 715 animal.age += 1; 716 } 717} 718 719@Entry 720@Component 721struct Demo1 { 722 @Provide dog:Animal = new Animal('WangCai', 'dog', 2); 723 724 changeAge2(animal:Animal) { 725 animal.age += 2; 726 } 727 728 build() { 729 Column({ space:10 }) { 730 Text(`Demo1: This is a ${this.dog.age}-year-old ${this.dog.type} named ${this.dog.name}.`) 731 .fontColor(Color.Red) 732 .fontSize(30) 733 Button('changeAge1') 734 .onClick(()=>{ 735 // 通过赋值添加 Proxy 代理 736 let a1 = this.dog; 737 Animal.changeAge1(a1); 738 }) 739 Button('changeAge2') 740 .onClick(()=>{ 741 // 通过赋值添加 Proxy 代理 742 let a2 = this.dog; 743 this.changeAge2(a2); 744 }) 745 Demo2() 746 } 747 } 748} 749 750@Component 751struct Demo2 { 752 753 build() { 754 Column({ space:10 }) { 755 Text(`Demo2.`) 756 .fontColor(Color.Blue) 757 .fontSize(30) 758 Demo3() 759 } 760 } 761} 762 763@Component 764struct Demo3 { 765 @Consume dog:Animal; 766 767 changeName2(animal:Animal) { 768 animal.name = 'White'; 769 } 770 771 build() { 772 Column({ space:10 }) { 773 Text(`Demo3: This is a ${this.dog.age}-year-old ${this.dog.type} named ${this.dog.name}.`) 774 .fontColor(Color.Yellow) 775 .fontSize(30) 776 Button('changeName1') 777 .onClick(()=>{ 778 // 通过赋值添加 Proxy 代理 779 let b1 = this.dog; 780 Animal.changeName1(b1); 781 }) 782 Button('changeName2') 783 .onClick(()=>{ 784 // 通过赋值添加 Proxy 代理 785 let b2 = this.dog; 786 this.changeName2(b2); 787 }) 788 } 789 } 790} 791``` 792