1# 自定义组件混用场景指导
2
3在\@Component装饰的自定义组件中(后续称V1的自定义组件),我们为开发者提供了与之对应的状态变量装饰器(后续称V1的装饰器),例如:\@State、\@Prop、\@Link等,然而状态管理V1(简称V1)对于嵌套类的观测存在诸多限制,例如需要开发者通过\@ObjectLink不断拆解嵌套类才能使得深层次数据具备观测能力。为此,在API12中为开发者开发了一套全新的状态管理V2(简称V2),开发者可以声明\@ComponentV2装饰的自定义组件(后续称V2的自定义组件)并搭配了一套全新的装饰器去使用(后续称V2的装饰器),如:\@Local、\@Param等。V2的提出不仅解决了V1对于嵌套类观测的先天性不足,同时对部分装饰器功能进行加强,如V2的\@Monitor对比V1的\@Watch不仅能感知变化后的数据,还能够获取变化之前的数据。
4
5在设计上,我们希望V1和V2的代码是完全隔离的,因为V1能实现的功能,V2能做的更好。但从实际角度出发,V1的开发者已经有很大的基础,让开发者一次性迁移成V2也不符合实际,因此在V1的代码中使用V2的部分能力是允许的,V2中也没有完全禁止V1,这样就涉及到V1和V2的一个混用问题,例如:V1的自定义组件使用了V2的自定义组件或V1去使用V2的装饰器等。
6
7本篇通过对V1、V2之间的混用场景进行较为完善的阐述,旨在指引开发者将V1代码向V2代码迁移。
8
9> **说明:**
10>
11> 状态管理V2从API version 12开始支持。
12
13## 概述
14
15状态管理V1与V2的混用规则可以概括为:
16
17* V1的自定义组件中不可以使用V2的装饰器,否则编译报错。
18
19* 组件间不存在变量传递时,V1的自定义组件中可以使用V2的自定义组件,包括import第三方\@ComponentV2装饰的自定义组件。
20
21* 组件间存在变量传递时,V1的变量传递给V2的自定义组件,有如下限制:
22  - V1中未被装饰器装饰的变量(后称普通变量):V2只能使用\@Param接收。
23  - V1中被装饰器装饰的变量(后称状态变量):V2存在只能通过\@Param装饰器接收,且仅限于boolean、number、enum、string、undefined、null这些简单类型数据。
24
25* V2的自定义组件中不可以使用V1的装饰器,否则编译报错。
26
27* 组件间不存在变量传递时,V2自定义组件可以使用V1的自定义组件,包括import第三方\@Component装饰的自定义组件。
28
29* 组件间存在变量传递时,V2的变量传递给V1的自定义组件,有如下限制:
30  - V2中未被装饰器装饰的变量(后称普通变量):若V1使用装饰器装饰接收的数据,只能通过\@State、\@Prop、\@Provide。
31  - V2中被装饰器装饰的变量(后称状态变量):若V1使用装饰器装饰接收的数据,不支持内置类型数据:Array、Set、Map、Date。
32
33## 状态管理装饰器总览
34
35### 状态管理V1的装饰器
36
37|  装饰器类别  |                            装饰器                            |
38| :----------: | :----------------------------------------------------------: |
39| 组件内装饰器 | \@State、\@Prop、\@Link、\@ObjectLink、\@Provide、\@Consume、\@StorageProp、\@StorageLink、\@LocalStorageProp、\@LocalStorageLink、\@Watch |
40| 类相关装饰器 |                     \@Observed、\@Track                      |
41
42### 状态管理V2的装饰器
43
44|  装饰器类别  |                            装饰器                            |
45| :----------: | :----------------------------------------------------------: |
46| 组件内装饰器 | \@Local、\@Param、\@Provider、\@Consumer、\@Once、\@Event、\@Monitor、\@Computed |
47| 类相关装饰器 |                \@ObservedV2、\@Trace、\@Type                 |
48
49### 状态管理装饰器支持的数据类型总览
50
51状态管理能够支持的数据类型有:
52
53| 数据类型     | 关键字                                             |
54| ------------ | -------------------------------------------------- |
55| 简单类型数据 | boolean、number、enum、string、null、undefined     |
56| function类型 | function(仅V2的\@Event、\@Monitor、\@Computed支持) |
57| Object类型   | Object                                             |
58| Class类型    | Class                                              |
59| 内置类型     | Array、Map、Set、Date                              |
60
61
62
63## 限制条件
64
65### V1和V2的装饰器不允许混用
66
67**1.V1的自定义组件中不可以使用V2的装饰器**
68
69```typescript
70@Component
71struct Child {
72  // @Param不可以在@Component中使用,编译报错
73  // @Once @Require都是@Param的能力扩展装饰器,必须和@Param一起连用
74  @Param message: string = "";
75  @Event changeMessage: (val: string) => void;  // @Event 不可以在@Component中使用,编译报错
76
77  build() {
78    Column() {
79      Text(this.message)
80        .fontSize(50)
81        .fontWeight(FontWeight.Bold)
82        .onClick(() => {
83          this.changeMessage('world hello');
84        })
85    }
86  }
87}
88
89@Entry
90@Component
91struct Index {
92  @Local message: string = 'Hello World'; // @Local不可以在 @Component中使用,编译报错
93
94  build() {
95    Column() {
96      Text(this.message)
97        .fontSize(50)
98        .fontWeight(FontWeight.Bold)
99      Divider()
100        .color(Color.Blue)
101      Child({
102        message: this.message,
103        changeMessage: (val: string) => {
104          this.message = val;
105        }
106      })
107    }
108    .height('100%')
109    .width('100%')
110  }
111}
112```
113
114V2的组件内装饰器不允许在V1的自定义组件中使用,编译会报错。
115
116\@Local、\@Param、\@Event,\@Provider、\@Consumer、\@Monitor、\@Computed和示例代码中的装饰器表现一致。
117
118**2.V2的自定义组件中不可以使用V1的装饰器**
119
120```typescript
121@ComponentV2
122struct Child {
123  @Prop message: string = "";  	// @Prop不可以在@ComponentV2中使用,编译报错
124  @Link myId: number;           // @Link不可以在@ComponentV2中使用,编译报错
125
126  build() {
127    Column() {
128      Text(this.message)
129        .fontSize(50)
130        .fontWeight(FontWeight.Bold)
131        .onClick(() => {
132          this.message = 'world hello';
133        })
134      Divider()
135        .color(Color.Blue)
136      Text(`${this.myId}`)
137        .id('HelloWorld')
138        .fontSize(50)
139        .fontWeight(FontWeight.Bold)
140        .onClick(() => {
141          this.myId++;
142        })
143    }
144  }
145}
146
147@Entry
148@ComponentV2
149struct Index {
150  @State message: string = 'Hello World';      // @State不可以在@ComponentV2中使用,编译报错
151  @State @Watch('idChange') myId: number = 1;  // @Watch不可以在@ComponentV2中使用,编译报错
152
153  idChange(propName: number) : void {
154    console.info(`id changed ${this.myId}`);
155  }
156
157  build() {
158    Column() {
159      Text(this.message)
160        .fontSize(50)
161        .fontWeight(FontWeight.Bold)
162      Divider()
163        .color(Color.Blue)
164      Child({
165        message: this.message,
166        myId: this.myId
167      })
168    }
169    .height('100%')
170    .width('100%')
171    .margin(5)
172  }
173}
174```
175
176V1的组件内装饰器不允许在V2的自定义组件中使用,编译会报错。
177
178\@ObjectLink、\@Provide、\@Consume、\@StorageProp、\@StorageLink、\@LocalStorageProp、\@LocalStorageLink和示例的装饰器表现一致。
179
180### 多个装饰器不允许装饰同一个变量(\@Watch、\@Once、\@Require除外)
181
182```typescript
183@Component
184struct Child {
185  @State @Prop message: string = "";	// 多个V1的装饰器不可以修饰同一个变量,编译器报错
186
187  build() {
188    Column() {
189      Text(this.message)
190        .fontSize(50)
191        .fontWeight(FontWeight.Bold)
192        .onClick(() => {
193          this.message = 'world hello';
194        })
195    }
196  }
197}
198
199@Entry
200@ComponentV2
201struct Index {
202  @Local @Param message: string = 'Hello World'; // 多个V2的装饰器不允许修饰同一个变量,编译器报错
203
204  build() {
205    Column() {
206      Text(this.message)
207        .fontSize(50)
208        .fontWeight(FontWeight.Bold)
209      Divider()
210        .color(Color.Blue)
211      Child({
212        message: this.message
213      })
214    }
215    .height('100%')
216    .width('100%')
217  }
218}
219```
220
221除了\@Watch、\@Once、\@Require这些能力扩展装饰器可以配合其他装饰器使用外,其他装饰器不允许装饰同一个变量。
222
223## 混用场景介绍
224
225### V1和V2类相关装饰器混用
226
227**1.V1的自定义组件中使用被\@ObservedV2装饰的类对象**
228
229```typescript
230@ObservedV2
231class Info {
232  @Trace myId: number;   		// 有观测能力
233  name: string;           		// 无观测能力
234  @Track trackId: number = 1; 	// @Track作为V1的装饰器,不能在@ObservedV2中使用,编译时报错;消除编译错误请去掉@Track
235
236  constructor(id?: number, name?: string) {
237    this.myId = id || 0;
238    this.name = name || 'aaa';
239  }
240}
241
242@Observed
243class message extends Info {	// 继承自@ObservedV2装饰的类不可以被Observed装饰,编译时报错;消除编译错误请去掉@Observed
244}
245
246class MessageInfo extends Info {
247}
248
249@Entry
250@Component
251struct Index {
252  info1: Info = new Info();                      // @ObservedV2装饰的Class可以在V1中使用,且被@Trace装饰的类属性具有观测能力
253  @State info2: Info = new Info();               // @ObservedV2装饰的Class不可以被V1的装饰器装饰,否则编译器报错;消除编译错误请去掉@State
254
255  @State messageInfo: MessageInfo = new MessageInfo();  // 继承自@ObservedV2的Class不可以被V1装饰器装饰,运行时报错;消除错误请去掉@State
256  build() {
257    Column() {
258      Text(`info1 name: ${this.info1.name}`)            // name未被@Trace装饰,无法观察变化
259        .fontSize(50)
260        .fontWeight(FontWeight.Bold)
261        .onClick(() => {
262          this.info1.name += 'b';
263        })
264      Text(`info1 id: ${this.info1.myId}`)              // myId被@Trace装饰,可观察变化
265        .fontSize(50)
266        .fontWeight(FontWeight.Bold)
267        .onClick(() => {
268          this.info1.myId += 1;
269        })
270      Divider()
271        .color(Color.Blue)
272      Text(`info2 id: ${this.info2.myId}`)
273        .fontSize(50)
274        .fontWeight(FontWeight.Bold)
275        .onClick(() => {
276          this.info2.myId += 1;
277        })
278      Divider()
279        .color(Color.Blue)
280      Text(`messageInfo id: ${this.messageInfo.myId}`) // 继承自@ObservedV2的Class被V1的装饰器装饰时会出现crash,运行时出错,需要去掉装饰器@State
281        .fontSize(50)
282        .fontWeight(FontWeight.Bold)
283        .onClick(() => {
284          this.messageInfo.myId += 1;
285        })
286    }
287    .height('100%')
288    .width('100%')
289    .margin(5)
290  }
291}
292```
293
294\@ObservedV2的使用需要遵循如下规则:
295
296* \@ObservedV2只能装饰Class,\@Trace、\@Type只能装饰类属性,且只能在\@ObservedV2中使用。
297* \@Track不可以在\@ObservedV2中使用。
298* 对于被\@ObservedV2装饰的Class,不可以直接被V1的装饰器装饰,否则编译时报错。
299* 示例中,开发者去掉报错的装饰器即可正常运行,被\@Trace装饰的类属性变化时可以观察到变化,否则不可以观测到变化。
300
301**2.V2的自定义组件中使用被\@Observed装饰的类对象**
302
303```typescript
304@Observed
305class Info {
306  @Track myId: number;   		  // 无观测能力,只能防止因其他属性改变而导致的连带刷新
307  name: string;           		  // 无观测能力
308  @Trace trackId: number = 1; 	  // @Trace作为V2的装饰器,不能在@Observed中使用,编译时报错;消除编译错误请去掉@Trace
309  constructor(id?: number, name?: string) {
310    this.myId = id || 0;
311    this.name = name || 'aaa';
312  }
313}
314
315@ObservedV2
316class message extends Info {      // @ObservedV2装饰的Class不能继承@Observed,编译时报错;消除编译错误请去掉@ObservedV2
317}
318
319class MessageInfo extends Info {
320}
321
322@Entry
323@ComponentV2
324struct Index {
325  info1: Info = new Info();             // @Observed装饰的Class可以在V2中使用
326  @Local info2: Info = new Info();      // @Observe装饰的Class不可以被V2的装饰器装饰,否则编译器报错;消除编译错误请去掉@Local
327  @Local messageInfo: MessageInfo = new MessageInfo();
328  build() {
329    Column() {
330      Text(`info1 name: ${this.info1.name}`)
331        .fontSize(50)
332        .fontWeight(FontWeight.Bold)
333        .onClick(() => {
334          this.info1.name += 'b';
335        })
336      Text(`info1 id: ${this.info1.myId}`)
337        .fontSize(50)
338        .fontWeight(FontWeight.Bold)
339        .onClick(() => {
340          this.info1.myId += 1;
341        })
342      Divider()
343        .color(Color.Blue)
344      Text(`info2 id: ${this.info2.myId}`)
345        .fontSize(50)
346        .fontWeight(FontWeight.Bold)
347        .onClick(() => {
348          this.info2.myId += 1;
349        })
350      Divider()
351        .color(Color.Blue)
352      // 继承自@ObservedV2的Class被V2装饰器装饰,V2的装饰器无类属性观测能力,所以不建议在V2中使用@Observed装饰的Class
353      Text(`messageInfo id: ${this.messageInfo.myId}`)
354        .fontSize(50)
355        .fontWeight(FontWeight.Bold)
356        .onClick(() => {
357          this.messageInfo.myId += 1;
358        })
359    }
360    .height('100%')
361    .width('100%')
362    .margin(5)
363  }
364}
365```
366
367不建议开发者在V2中使用\@Observed装饰的Class,因为\@Observed和\@Track仅能对类属性做区分,无观测能力,使用\@Observed和\@ObjectLink拆分嵌套数据才能够观测深层次数据,但\@ObjectLink无法在V2的自定义组件中使用。
368
369开发者在对V1的代码向V2迁移时,\@Observed装饰的Class不建议在\@ComponentV2中使用,无观测能力,如果一定要使用,则遵循以下规则:
370
371* \@Observed只能装饰Class,且\@Trace不可以在\@Observed中使用。
372* \@Observed和\@Track无任何观测能力,只能用于防止Class中一个类属性改变而导致整个Class的刷新。
373* 继承自\@Observed的Class被V2装饰器装饰,V2的组件内装饰器无类属性观测能力,所以使用\@Observed会无法观测到类属性变化。
374* 示例中,开发者去掉报错的装饰器即可正常运行,由于无观测能力,所以不建议V2中使用\@Observed。
375
376### 不存在变量传递时,V1和V2的自定义组件混用
377
378**1.V1中使用V2的自定义组件**
379
380```typescript
381@ComponentV2
382struct Child {
383  @Local message: string = "hello";
384
385  build() {
386    Column() {
387      Text(this.message)
388        .fontSize(50)
389        .fontWeight(FontWeight.Bold)
390        .onClick(() => {
391          this.message = 'world';
392        })
393    }
394  }
395}
396
397@Entry
398@Component
399struct Index {
400  @State message: string = 'Hello World';
401
402  build() {
403    Column() {
404      Text(this.message)
405        .fontSize(50)
406        .fontWeight(FontWeight.Bold)
407        .onClick(() => {
408          this.message = 'world hello';
409        })
410      Divider()
411        .color(Color.Blue)
412      Child()
413    }
414    .height('100%')
415    .width('100%')
416  }
417}
418```
419
420V1中使用V2的自定义组件,当不存在变量传递时无影响,若涉及变量传递,请见下一节V1和V2的数据混用。
421
422**2.V2中使用V1的自定义组件**
423
424```typescript
425@Component
426struct Child {
427  @State message: string = "hello";
428
429  build() {
430    Column() {
431      Text(this.message)
432        .fontSize(50)
433        .fontWeight(FontWeight.Bold)
434        .onClick(() => {
435          this.message = 'world';
436        })
437    }
438  }
439}
440
441@Entry
442@ComponentV2
443struct Index {
444  @Local message: string = 'Hello World';
445
446  build() {
447    Column() {
448      Text(this.message)
449        .fontSize(50)
450        .fontWeight(FontWeight.Bold)
451        .onClick(() => {
452          this.message = 'world hello';
453        })
454      Divider()
455        .color(Color.Blue)
456      Child()
457    }
458    .height('100%')
459    .width('100%')
460  }
461}
462```
463
464V2中使用V1的自定义组件,当不存在变量传递时无影响,若涉及变量传递,请见下一节V1和V2的数据混用。
465
466### 存在变量传递时,V1和V2的自定义组件数据混用
467
468**1.V1->V2:V1的普通变量传递给V2的自定义组件**
469
470```typescript
471class Info {
472  myId: number;
473  name: string;
474
475  constructor(myId?: number, name?: string) {
476    this.myId = myId || 0;
477    this.name = name || 'aaa';
478  }
479}
480
481@ComponentV2
482struct Child {
483  // V2对数据输入有严格的管理,从父组件接受数据时,必须@Param装饰器进行数据接收
484  @Param @Once message: string = "hello";	              // 可以观测到变化,同步回父组件依赖@Event,使用了@Once可以修改@Param装饰的变量
485  @Param @Once undefineVal: string | undefined = undefined;  // 使用了@Once可以修改@Param装饰的变量
486  @Param info: Info = new Info();		                 // 观测不到类属性变化
487  @Require @Param set: Set<number>;
488
489  build() {
490    Column() {
491      Text(`child message:${this.message}`) // 显示 string
492        .fontSize(30)
493        .fontWeight(FontWeight.Bold)
494        .onClick(() => {
495          this.message = 'world';
496        })
497
498      Divider()
499        .color(Color.Blue)
500      Text(`undefineVal:${this.undefineVal}`) // 显示 undefineVal
501        .fontSize(30)
502        .fontWeight(FontWeight.Bold)
503        .onClick(() => {
504          this.undefineVal = "change to define";
505        })
506      Divider()
507        .color(Color.Blue)
508      Text(`info id:${this.info.myId}`) // 显示 info:myId
509        .fontSize(30)
510        .fontWeight(FontWeight.Bold)
511        .onClick(() => {
512          this.info.myId++;
513        })
514      Divider()
515        .color(Color.Blue)
516      ForEach(Array.from(this.set.values()), (item: number) => {  // 显示 Set
517        Text(`${item}`)
518          .fontSize(30)
519      })
520    }
521    .margin(5)
522  }
523}
524
525@Entry
526@Component
527struct Index {
528  message: string = 'Hello World';       // 简单数据
529  undefineVal: undefined = undefined;    // 简单类型,undefined
530  info: Info = new Info();               // Class类型
531  set: Set<number> = new Set([10, 20]);  // 内置 类型
532
533  build() {
534    Column() {
535      Text(`message:${this.message}`)
536        .fontSize(30)
537        .fontWeight(FontWeight.Bold)
538        .onClick(() => {
539          this.message = 'world hello';
540        })
541      Divider()
542        .color(Color.Blue)
543      Child({
544        message: this.message,
545        undefineVal: this.undefineVal,
546        info: this.info,
547        set: this.set
548      })
549    }
550    .height('100%')
551    .width('100%')
552  }
553}
554```
555
556当V1的普通变量传递给V2的自定义组件时,有如下限制:
557
558* V2的自定义组件必须通过\@Param接收数据。
559* 接收数据的观测能力为\@Param能力,对于接收的Class,需要通过\@ObservedV2和\@Trace才能观察变化。
560
561**2.V1->V2:V1的状态变量传递给V2的自定义组件**
562
563```typescript
564class Info {
565  myId: number;
566  name: string;
567
568  constructor(myId?: number, name?: string) {
569    this.myId = myId || 0;
570    this.name = name || 'aaa';
571  }
572}
573
574@ComponentV2
575struct Child {
576  // V2对数据输入有严格的管理,从父组件接受数据时,必须@Param装饰器进行数据接收
577  @Param @Once message: string = "hello";
578  @Param @Once undefineVal: string | undefined = undefined;  // 使用了@Once可以修改@Param装饰的变量
579  @Param info: Info = new Info();
580  @Require @Param set: Set<number>;
581  build() {
582    Column() {
583      Text(`child message:${this.message}`) // 显示string
584        .fontSize(30)
585        .fontWeight(FontWeight.Bold)
586        .onClick(() => {
587          this.message = 'world';
588        })
589      Divider()
590        .color(Color.Blue)
591      Text(`undefineVal:${this.undefineVal}`) // 显示undefineVal
592        .fontSize(30)
593        .fontWeight(FontWeight.Bold)
594        .onClick(() => {
595          this.undefineVal = "change to define";
596        })
597      Divider()
598        .color(Color.Blue)
599      Text(`info id:${this.info.myId}`) // 显示info:myId
600        .fontSize(30)
601        .fontWeight(FontWeight.Bold)
602        .onClick(() => {
603          this.info.myId++;
604        })
605      Divider()
606        .color(Color.Blue)
607      ForEach(Array.from(this.set.values()), (item: number) => {  // 显示Set
608        Text(`${item}`)
609          .fontSize(30)
610      })
611    }
612    .margin(5)
613  }
614}
615
616@Entry
617@Component
618struct Index {
619  @State message: string = 'Hello World';       // 简单类型数据,支持
620  @State undefineVal: undefined = undefined;    // 简单类型数据,undefined,支持
621  @State info: Info = new Info();               // Class类型,不支持传递,编译器报错;消除编译错误请去掉@State
622  @State set: Set<number> = new Set([10, 20]);  // 内置类型,不支持传递,编译器报错;消除编译错误请去掉@State
623
624  build() {
625    Column() {
626      Text(`message:${this.message}`)
627        .fontSize(30)
628        .fontWeight(FontWeight.Bold)
629        .onClick(() => {
630          this.message = 'world hello';
631        })
632      Divider()
633        .color(Color.Blue)
634      Child({
635        message: this.message,
636        undefineVal: this.undefineVal,
637        info: this.info,
638        set: this.set
639      })
640    }
641    .height('100%')
642    .width('100%')
643  }
644}
645```
646
647当V1的状态变量给V2的自定义组件时,有如下规则:
648
649* 仅支持简单类型变量,其余类型数据会在编译时报错。
650
651* 示例中使用了\@State装饰器,\@Prop、\@Link、\@ObjectLink、\@Provide、\@Consume、\@StorageProp、\@StorageLink、\@LocalStorageProp、\@LocalStorageLink行为和\@State保持一致。
652
653**3.V2->V1:V2的普通变量传递给V1的自定义组件**
654
655```typescript
656class Info {
657  myId: number;
658  name: string;
659
660  constructor(myId?: number, name?: string) {
661    this.myId = myId || 0;
662    this.name = name || 'aaa';
663  }
664}
665
666@Component
667struct Child {
668  // V1从V2接收的状态变量,若使用装饰器,仅可使用@State、@Prop、@Provide接收
669  @State  message: string = "hello";	         // 可以观测到变化
670  @State info: Info = new Info();		      	// 可以观测一层类属性变化
671  @Prop undefineVal: undefined | string = undefined;
672  @Provide setMap: Set<number> = new Set();
673  build() {
674    Column() {
675      Text(`child message:${this.message}`) 	// 显示string
676        .fontSize(30)
677        .fontWeight(FontWeight.Bold)
678        .onClick(() => {
679          this.message = 'world';
680        })
681      Divider()
682        .color(Color.Blue)
683      Text(`undefineVal:${this.undefineVal}`) 	// 显示undefineVal
684        .fontSize(30)
685        .fontWeight(FontWeight.Bold)
686        .onClick(() => {
687          this.undefineVal = "change to define";
688        })
689      Divider()
690        .color(Color.Blue)
691      Text(`info id:${this.info.myId}`)		 	// 显示info:myId
692        .fontSize(30)
693        .fontWeight(FontWeight.Bold)
694        .onClick(() => {
695          this.info.myId++;
696        })
697      Divider()
698        .color(Color.Blue)
699      ForEach(Array.from(this.setMap.values()), (item: number) => {  // 显示 Set
700        Text(`${item}`)
701          .fontSize(30)
702      })
703    }
704    .margin(5)
705  }
706}
707
708@Entry
709@ComponentV2
710struct Index {
711  message: string = 'Hello World';       // 简单数据类型
712  undefineVal: undefined = undefined;    // 简单数据类型,undefined
713  info: Info = new Info();               // Class类型
714  set: Set<number> = new Set([10, 20]);  // 内置 类型
715
716  build() {
717    Column() {
718      Text(`message:${this.message}`)
719        .fontSize(30)
720        .fontWeight(FontWeight.Bold)
721        .onClick(() => {
722          this.message = 'world hello';
723        })
724      Divider()
725        .color(Color.Blue)
726      Child({
727        message: this.message,
728        undefineVal: this.undefineVal,
729        info: this.info,
730        setMap: this.set
731      })
732    }
733    .height('100%')
734    .width('100%')
735  }
736}
737```
738
739当V2的普通变量传递给V1自定义组件时:
740
741* V1可以不使用装饰器接收数据,接收过来的变量在V1组定义组件内也会是普通变量。
742
743* V1若使用装饰器接收数据,仅可通过\@State、\@Prop、\@Provide接收。
744
745**4.V2->V1:V2的状态变量传递给V1的自定义组件**
746
747```typescript
748class Info {
749  myId: number;
750  name: string;
751
752  constructor(myId?: number, name?: string) {
753    this.myId = myId || 0;
754    this.name = name || 'aaa';
755  }
756}
757
758@Component
759struct Child {
760  // V1从V2接收的状态变量,仅可使用@State、@Prop、@Provide接收
761  @State  message: string = "hello";	        // 可以观测到变化
762  @State info: Info = new Info();		        // 可以观测一层类属性变化
763  @Prop undefineVal: undefined | string = undefined;
764  @Provide set: Set<number> = new Set();
765  build() {
766    Column() {
767      Text(`child message:${this.message}`) 	// 显示 string
768        .fontSize(30)
769        .fontWeight(FontWeight.Bold)
770        .onClick(() => {
771          this.message = 'world';
772        })
773      Divider()
774        .color(Color.Blue)
775      Text(`undefineVal:${this.undefineVal}`) 	// 显示 undefineVal
776        .fontSize(30)
777        .fontWeight(FontWeight.Bold)
778        .onClick(() => {
779          this.undefineVal = "change to define";
780        })
781      Divider()
782        .color(Color.Blue)
783      Text(`info id:${this.info.myId}`) 	// 显示 info:myId
784        .fontSize(30)
785        .fontWeight(FontWeight.Bold)
786        .onClick(() => {
787          this.info.myId++;
788        })
789
790      Divider()
791        .color(Color.Blue)
792      ForEach(Array.from(this.set.values()), (item: number) => {  // 显示 Set
793        Text(`${item}`)
794          .fontSize(30)
795      })
796    }
797    .margin(5)
798  }
799}
800
801@Entry
802@ComponentV2
803struct Index {
804  @Local message: string = 'Hello World';       	// 简单数据类型,支持传递
805  @Provider() undefineVal: undefined = undefined;   // 简单数据类型,undefined,支持传递
806  @Consumer() info: Info = new Info();          	// Class类型,支持传递
807  @Param set: Set<number> = new Set([10, 20]);  	// 内置类型,不支持传递;消除编译错误请去掉@Param
808
809  build() {
810    Column() {
811      Text(`message:${this.message}`)
812        .fontSize(30)
813        .fontWeight(FontWeight.Bold)
814        .onClick(() => {
815          this.message = 'world hello';
816        })
817
818      Divider()
819        .color(Color.Blue)
820      Child({
821        message: this.message,
822        undefineVal: this.undefineVal,
823        info: this.info,
824        set: this.set
825      })
826    }
827    .height('100%')
828    .width('100%')
829  }
830}
831```
832
833V2的状态变量传递给V1的自定义组件,存在如下限制:
834
835* V1可以不使用装饰器接收数据,接收过来的变量在V1组定义组件内也会是普通变量。
836
837* V1若使用装饰器接收数据,仅可通过\@State、\@Prop、\@Provide接收。
838* V1若使用装饰器接收数据,不支持内置类型的数据。
839
840### 混用场景总结
841
842通过对V1和V2的混用场景详细梳理,可以看到,当V2的代码混用V1的代码时,即V1的组件或者类数据向V2进行传递,大部分V1的能力在V2都是被禁止的。而V1的代码去混用V2代码时,即V2的组件或者类数据向V1传递,做了部分功能开放,例如\@ObservedV2和\@Trace,这也是对V1嵌套类数据的观测能提供的最大的帮助。所以在代码开发时,不鼓励开发者使用V1和V2进行混用开发,但是对于代码迁移上,可以让V1的开发者逐步将代码向V2进行迁移,从而稳步替换V1的功能代码,并且十分不鼓励开发者在V2的代码架构上混用V1的代码。
843
844## 补充场景
845
846\@Observed和\@ObservedV2由于装饰Class类型,而Class可以进行多层级的嵌套,因此场景相对复杂,本节主要是对Class类型的自嵌套和内置类型的嵌套作一个详细的场景说明。由于\@Observed并没有\@ObservedV2+@Trace那样强大的深层次观测能力,不再对\@Observed的深层次嵌套进行讨论,只讨论\@ObservedV2在V1的使用场景。
847
848### 使用\@Observed+\@ObjectLink观测嵌套类
849
850```typescript
851@Observed
852class Info {
853  myId: number;
854  name: string;
855
856  constructor(myId?: number, name?: string) {
857    this.myId = myId || 0;
858    this.name = name || 'aaa';
859  }
860}
861
862@Observed
863class MessageInfo { 		// 一层嵌套
864  @Track info: Info;        // 防止messageId改变导致info的连带刷新
865  @Track messageId: number; // 防止messageId改变导致info的连带刷新
866
867  constructor(info?: Info, messageId?: number) {
868    this.info = info || new Info();
869    this.messageId = messageId || 0;
870  }
871}
872
873@Observed
874class MessageInfoNested {	 // 二层嵌套
875  messageInfo: MessageInfo;
876
877  constructor(messageInfo?: MessageInfo) {
878    this.messageInfo = messageInfo || new MessageInfo();
879  }
880}
881
882@Component
883struct GrandSon {
884  @ObjectLink info: Info;
885
886  build() {
887    Column() {
888      Text(`ObjectLink info info.myId:${this.info.myId}`)  // 经过@ObjectLink拆解两次之后,观测到变化
889        .fontSize(30)
890        .onClick(() => {
891          this.info.myId++;
892        })
893    }
894  }
895}
896
897@Component
898struct Child {
899  @ObjectLink messageInfo: MessageInfo;
900
901  build() {
902    Column() {
903      Text(`ObjectLink MessageInfo messageId:${this.messageInfo.messageId}`)  // 经过@ObjectLink拆解之后,可以观测一层类属性变化
904        .fontSize(30)
905        .onClick(() => {
906          this.messageInfo.messageId++;
907        })
908      Divider()
909        .color(Color.Blue)
910      Text(`ObjectLink MessageInfo info.myId:${this.messageInfo.info.myId}`)  // 经过@ObjectLink拆解之后,依旧观测不到变化
911        .fontSize(30)
912        .onClick(() => {
913          this.messageInfo.info.myId++;
914        })
915      GrandSon({info: this.messageInfo.info});				// 继续拆解一层子组件
916    }
917  }
918}
919
920
921
922@Entry
923@Component
924struct Index {
925  @State messageInfoNested: MessageInfoNested = new MessageInfoNested();  // 三层嵌套的数据,需要对所有数据进行观测。
926
927  build() {
928    Column() {
929      // 观察messageInfoNested
930      Text(`messageInfoNested messageId:${this.messageInfoNested.messageInfo.messageId}`)  // @State只有一层类属性观测能力,无法观察到变化
931        .fontSize(30)
932        .onClick(() => {
933          this.messageInfoNested.messageInfo.messageId++;
934        })
935      Divider()
936        .color(Color.Blue)
937      // 通过@ObjectLink嵌套观察 messageInfoId
938      Child({messageInfo: this.messageInfoNested.messageInfo})      // 经过拆分后,使用@ObjectLink拆分可以观察到深一层的变化
939      Divider()
940        .color(Color.Blue)
941    }
942    .height('100%')
943    .width('100%')
944    .margin(10)
945  }
946}
947```
948
949示例给出了一个三层嵌套的场景,可以看到:
950
951* V1装饰器的观测能力是对数据本身做代理,因此当数据存在嵌套时,V1只能通过\@Observed+\@ObjectLink的方式进行拆分子组件观测深层次数据。
952* \@Track是用来防止MessageInfo类中的info被messageId改变而连带刷新,开发者去掉\@Track可观测到,当messageId改变时,info的连带改变,但是这并不是\@ObjectLink的观测能力。
953
954### 使用@ObsevedV2+@Trace观测嵌套类
955
956```typescript
957@ObservedV2
958class Info {
959  @Trace myId: number;
960  name: string;
961
962  constructor(myId?: number, name?: string) {
963    this.myId = myId || 0;
964    this.name = name || 'aaa';
965  }
966}
967
968@Observed
969class MessageInfo { // 一层嵌套
970  @Track info: Info;        // 防止messageId改变导致info的连带刷新
971  @Track messageId: number; // 防止messageId改变导致info的连带刷新
972
973  constructor(info?: Info, messageId?: number) {
974    this.info = info || new Info();   // 使用传入的info或创建一个新的Info
975    this.messageId = messageId || 0;
976  }
977}
978
979@Observed
980class MessageInfoNested { // 二层嵌套,MessageInfoNested如果是被@ObservedV2装饰,则不可以被V1的状态变量更新相关的装饰器装饰,如@State
981  messageInfo: MessageInfo;
982
983  constructor(messageInfo?: MessageInfo) {
984    this.messageInfo = messageInfo || new MessageInfo();
985  }
986}
987
988@Component
989struct Child {
990  @ObjectLink messageInfo: MessageInfo;
991
992  build() {
993    Column() {
994      Text(`ObjectLink MessageInfo messageId:${this.messageInfo.messageId}`)  // 经过@ObjectLink拆解之后,可以观测一层类属性变化
995        .fontSize(30)
996        .onClick(() => {
997          this.messageInfo.messageId++;
998        })
999    }
1000  }
1001}
1002
1003@Entry
1004@Component
1005struct Index {
1006  @State messageInfoNested: MessageInfoNested = new MessageInfoNested();  // 三层嵌套的数据,如何观测内部。
1007
1008  build() {
1009    Column() {
1010      // 观察messageInfoNested,@State只有一层观测能力,无法观察到变化
1011      Text(`messageInfoNested messageId:${this.messageInfoNested.messageInfo.messageId}`)
1012        .fontSize(30)
1013        .onClick(() => {
1014          this.messageInfoNested.messageInfo.messageId++;
1015        })
1016      Divider()
1017        .color(Color.Blue)
1018      Text(`messageInfoNested name:${this.messageInfoNested.messageInfo.info.name}`)   // 未被@Trace修饰,无法观测
1019        .fontSize(30)
1020        .onClick(() => {
1021          this.messageInfoNested.messageInfo.info.name += 'a';
1022        })
1023      Divider()
1024        .color(Color.Blue)
1025      Text(`messageInfoNested myId:${this.messageInfoNested.messageInfo.info.myId}`)   // 被@Trace修饰,无论嵌套多少层都能观测
1026        .fontSize(30)
1027        .onClick(() => {
1028          this.messageInfoNested.messageInfo.info.myId++;
1029        })
1030      Divider()
1031        .color(Color.Blue)
1032      // 通过@ObjectLink嵌套观察 messageInfoId
1033      Child({messageInfo: this.messageInfoNested.messageInfo})      // 经过拆分后,使用@ObjectLink拆分可以观察到深一层的变化
1034    }
1035    .height('100%')
1036    .width('100%')
1037    .margin(10)
1038  }
1039}
1040```
1041
1042当使用\@observedV2 + \@Trace可以发现:
1043
1044* \@observedV2 + \@Trace将观测能力嵌套到类属性上,所以当类属性被@Trace标记时,无论嵌套多少层都可以观测到变化。
1045* \@ObservdV2和\@Observed嵌套混用时,类对象能否被V1的装饰器装饰取决于最外层Class所使用的类装饰器,例如示例中嵌套在深处的\@ObservedV2装饰的Class不影响最外层的Class被V1的装饰器装饰。