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