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![zh-cn_image_0000001552614217](figures/zh-cn_image_0000001552614217.png)
77
78
79| \@Consume传递/访问 | 说明                                       |
80| -------------- | ---------------------------------------- |
81| 从父组件初始化和更新     | 禁止。通过相同的变量名和alias(别名)从\@Provide初始化。      |
82| 用于初始化子组件       | 允许,可用于初始化\@State、\@Link、\@Prop、\@Provide。 |
83| 和祖先组件同步        | 和\@Provide双向同步。                          |
84| 是否支持组件外访问      | 私有,仅可以在所属组件内访问                           |
85
86
87  **图2** \@Consume初始化规则图示  
88
89
90![zh-cn_image_0000001502094666](figures/zh-cn_image_0000001502094666.png)
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![Provide_Consume_framework_behavior](figures/Provide_Consume_framework_behavior.png)
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.agethis.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