• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 状态管理V1V2混用文档
2<!--Kit: ArkUI-->
3<!--Subsystem: ArkUI-->
4<!--Owner: @liwenzhen3-->
5<!--Designer: @s10021109-->
6<!--Tester: @TerryTsao-->
7<!--Adviser: @zhang_yixin13-->
8
9## 概述
10
11在状态管理框架的演进过程中,分别于API version 7和API version 12推出了状态管理V1和V2两个版本。对于已经使用状态管理V1的应用,如果有诉求向状态管理V2迁移,可参考[状态管理V1和V2迁移文档](./arkts-v1-v2-migration.md)。
12
13对于大型应用,迁移过程中会遇到V1V2混用的场景,在API version 19之前,混用场景有相对严格的校验,主要表现在复杂对象的传递上,具体规则可参考[自定义组件混用场景指导](./arkts-custom-component-mixed-scenarios.md)。为了帮助开发者顺利地向V2迁移,从API version 19开始,减少了对V1V2混用场景的约束。具体变更可参考下表。同时提供新的方法[enableV2Compatibility](../../reference/apis-arkui/js-apis-StateManagement.md#enablev2compatibility19)和[makeV1Observed](../../reference/apis-arkui/js-apis-StateManagement.md#makev1observed19)来帮助开发者解决在迁移过程中遇到的混用问题。
14
15> **说明:**
16>
17> 本文档中使用“->”表示变量的传递,比如“V1->V2”,表示V1状态变量向V2状态变量传递。
18
19
20## 校验规则
21在API version 19以前,状态管理V1V2的混用规则可以总结为:
221. V1装饰器不能和[@ObserveV2](./arkts-new-observedV2-and-trace.md)一起使用。
232. V2装饰器不能和[@Observed](./arkts-observed-and-objectlink.md)一起使用。
243. V1->V2只能传简单类型,不允许传复杂类型,包括built-in类型Array、Map、Set、Date。
254. V2->V1可以传简单类型和普通class,不允许传built-in类型Array、Map、Set、Date。
26
27从API version 19开始,仅第1条规则依旧禁止,第2-4条规则均放开校验。具体编译期校验见下表。
28
29| 场景  | API version 19以前 | API version 19及以后  |
30|------|----|------|
31| V1装饰器和\@ObservedV2同时使用   | 报错 | 报错 |
32| V2装饰器和\@Observed同时使用 | 报错 | 不报错 |
33| V1->V2 普通class  | 报错 | 不报错 |
34| V1->V2 built-in类型Array、Map、Set、Date  | 报错 | 不报错 |
35| V1->V2 \@Observed装饰的class  | 报错 | 不报错 |
36| V2->V1 \@ObservedV2装饰的class  | 报错 | 报错 |
37| V2->V1 built-in类型Array、Map、Set、Date  | 报错 | 不报错 |
38| \@ObjectLink被非\@Observed装饰的class初始化  | 报错 | 不报错 |
39
40依旧禁止第1条,是因为\@ObservedV2/\@Trace有自己独立的观察能力,不仅可以在\@ComponentV2中使用,也可以独立在\@Component中使用,状态管理框架不希望其观察能力和V1的观察能力混合使用,所以依旧维持禁止现状。
41
42## 新增接口
43### makeV1Observed
44
45static makeV1Observed\<T extends object\>(source: T): T
46
47[makeV1Observed](../../reference/apis-arkui/js-apis-StateManagement.md#makev1observed19)将不可观察的对象包装成状态管理V1可观察的对象,能力等同于@Observed,其返回值可初始化@ObjectLink。
48
49>**说明:**
50>
51>从API version 19开始,开发者可以使用UIUtils中的makeV1Observed接口将不可观察的对象包装成状态管理V1可观察的对象。
52
53**接口说明**
54- makeV1Observed主要和enableV2Compatibility搭配使用,实现V2->V1的传递。
55- makeV1Observed可将普通class、Array、Map、Set、Date类型转换为V1的状态变量,其能力等同于\@Observed,所以其返回值可以初始化\@ObjectLink。
56- 如果makeV1Observed接受的数据已经是V1的状态变量,则返回自身,不做任何改变。
57- makeV1Observed不会递归执行,仅会将第一层包装成V1的状态变量。
58
59**限制条件**
60- 不支持[collections类型](../../reference/apis-arkts/arkts-apis-arkts-collections.md)和[\@Sendable](../../arkts-utils/arkts-sendable.md)装饰的class。
61- 不支持非object类型。
62- 不支持undefined、null。
63- 不支持\@ObservedV2、[makeObserved](../../reference/apis-arkui/js-apis-StateManagement.md#makeobserved)的返回值和V2装饰器装饰的built-in类型的变量(Array、Map、Set和Date)。
64
65
66### enableV2Compatibility
67
68static enableV2Compatibility\<T extends object\>(source: T): T
69
70[enableV2Compatibility](../../reference/apis-arkui/js-apis-StateManagement.md#enablev2compatibility19)将V1的状态变量使能V2的观察能力,即让V1状态变量可以在\@ComponentV2中观察到变化。
71
72>**说明:**
73>
74>从API version 19开始,开发者可以使用UIUtils中的enableV2Compatibility接口将V1的状态变量兼容V2中使用。
75
76**接口说明**
77- 该接口主要应用于V1->V2的场景,V1的状态变量调用该接口后,传递到\@ComponentV2中,则可以在V2中观察到变化,从而实现数据的联动刷新。
78- enableV2Compatibility只能作用于V1的状态变量。V1状态变量为V1装饰器装饰的变量,即\@Observed装饰的变量,或\@State、\@Prop、\@Link、\@Provide、\@Consume和\@ObjectLink(\@ObjectLink需是\@Observed装饰的实例或者makeV1Observed的返回值)装饰的变量。否则,将返回入参自身。
79- enableV2Compatibility会递归遍历class的所有属性,Array/Set/Map的所有子项,直到遇到非V1状态变量的数据,则停止当前分支的遍历。
80
81**限制条件**
82- 不支持非object类型。
83- 不支持undefined、null。
84- 不支持非V1的状态变量数据。
85- 不支持\@ObservedV2、[makeObserved](../../reference/apis-arkui/js-apis-StateManagement.md#makeobserved)的返回值和V2装饰器装饰的built-in类型的变量(Array、Map、Set和Date)。
86
87## 混用范式
88
89基于[enableV2Compatibility](../../reference/apis-arkui/js-apis-StateManagement.md#enablev2compatibility19)和[makeV1Observed](../../reference/apis-arkui/js-apis-StateManagement.md#makev1observed19)接口,V1V2混用范式如下:
90
91### V1->V2
92- V1的状态变量传递给V2的\@Param,调用`UIUtils.enableV2Compatibility`使V1的状态变量可在\@ComponentV2中有观察能力。完整例子见[常见场景](#常见场景)。
93```ts
94import { UIUtils } from '@kit.ArkUI';
95
96@Observed
97class ObservedClass {
98}
99
100@Entry
101@Component
102struct CompV1 {
103  @State observedClass: ObservedClass = new ObservedClass();
104
105  build() {
106    Column() {
107      CompV2({ observedClass: UIUtils.enableV2Compatibility(this.observedClass) })
108    }
109  }
110}
111
112@ComponentV2
113struct CompV2 {
114  @Param observedClass: ObservedClass = new ObservedClass();
115
116  build() {
117  }
118}
119```
120- V1状态变量可观察第一层属性,在调用`UIUtils.enableV2Compatibility`传递给\@Param后,\@Param也可观察第一层属性的变化。
121
122具体场景能力可见下表。
123
124| \@Component(父) -> \@ComponentV2(子)  | 调用enableV2Compatibility后观察行为 |
125|------|----|
126| 常规变量| 无观察能力,因为enableV2Compatibility仅支持V1状态变量。 |
127| \@Observed装饰class   | 可观察第一层属性。 |
128| V1装饰器装饰的变量,其类型为Array、Map、Set和Date  | 可观察API调用。 |
129| V1装饰器装饰的变量,其类型为非\@Observed装饰的class  | 可观察第一层属性。需注意,如果数据源是\@ObjectLink,则其需要为\@Observed装饰class的实例或者makeV1Observed的返回值。 |
130| 普通Array,其数组项为\@Observed装饰的class  | 不可观察,因为enableV2Compatibility检查外层数组为非V1状态变量,所以接口不生效,返回数据源本身。 |
131| V1装饰器装饰的变量,其类型为普通Array,其数组项为\@Observed装饰的class  | 在\@Component中仅可观察第一层,如果想深度观察,则需搭配\@ObjectLink使用。在\@ComponentV2中可深度观察。 |
132| \@ObservedV2装饰的class  | 在V1和V2中可观察,其观察能力源于\@ObservedV2和\@Trace的能力,enableV2Compatibility不生效。 |
133| V1装饰器装饰的变量,其类型为普通Array,其数组项为\@ObservedV2装饰的class | 可观察,因为enableV2Compatibility会使外层的Array在V2中可观察。\@ObservedV2装饰的class的属性观察能力源自于\@ObservedV2和\@Trace,和enableV2Compatibility无关。 |
134
135### V2->V1
136
137在V2->V1时,推荐联合使用`UIUtils.enableV2Compatibility(UIUtils.makeV1Observed())`。如果当前对象已经是V1的可观察数据了,则仅调用`UIUtils.enableV2Compatibility`即可,完整例子见[常见场景](#常见场景)。
138
139```ts
140import { UIUtils } from '@kit.ArkUI';
141
142@Observed
143class ObservedClass {}
144
145@Entry
146@ComponentV2
147struct CompV2 {
148  @Local observedClass: ObservedClass = UIUtils.enableV2Compatibility(new ObservedClass());
149  build() {
150    Column() {
151      CompV1({ observedClass: this.observedClass })
152    }
153  }
154}
155
156@Component
157struct CompV1 {
158  @ObjectLink observedClass: ObservedClass;
159  build() {}
160}
161```
162
163具体场景如下表。
164
165| \@ComponentV2(父) -> \@Component(子)  | 调用enableV2Compatibility后观察行为 |
166|------|----|
167| \@Observed装饰class的嵌套类 | 在\@ComponentV2可深度观察嵌套属性的变化。 |
168| 普通class  | 可以观察,需要调用`makeV1Observed`使得`enableV2Compatibility`正常工作。 |
169| Array\<number\>,或其他简单类型数组  | 可以观察,需要调用`makeV1Observed`。</br>例子: `@Local local : Array<number> = UIUtils.enableV2Compatibility(UIUtils.makeV1Observed([1, 2, 3]))`。 |
170| Array\<ObservedClass\>,即数组项是\@Observed装饰的class  | 可以观察,需要调用`makeV1Observed`。</br>例子: `@Local local : Array<ObservedClass> = UIUtils.enableV2Compatibility(UIUtils.makeV1Observed([new ObservedClass()]))`。 |
171|  Array\<Array\<number\>\>,二维数组,数组项或为其他简单类型 | 可以观察,需要调用`makeV1Observed`。</br>例子: `@Local local : Array<Array<number>>> = UIUtils.enableV2Compatibility(UIUtils.makeV1Observed([UIUtils.makeV1Observed([1, 2, 3])]))`。|
172
173
174## 混用规则
175- V1->V2传递复杂类型数据,需要调用`enableV2Compatibility`,否则无法实现V1和V2的数据联动,推荐在V2组件的构造处调用,否则当变量被整体赋值时,需要再次手动调用`enableV2Compatibility`。
176
177```ts
178// 推荐,this.state = new ObservedClass()时无需再调用UIUtils.enableV2Compatibility,减少代码量
179SubComponentV2({param: UIUtils.enableV2Compatibility(this.state)})
180
181// 不推荐,state做整体赋值时,需要再次调用UIUtils.enableV2Compatibility
182// 否则传递给SubComponentV2的V1变量是无法在V2中观察的
183// @State state: ObservedClass = UIUtils.enableV2Compatibility(new ObservedClass());
184// this.state = UIUtils.enableV2Compatibility(new ObservedClass())
185SubComponentV2({param: this.state})
186```
187
188- V2->V1传递复杂类型数据,在V2中优先声明成V1的状态变量数据,并调用`UIUtils.enableV2Compatibility`。因为在状态管理V1中,状态变量默认有观察第一层的能力,而状态管理V2仅有观察自身的能力,如果希望双方数据联动,则需要调用`UIUtils.enableV2Compatibility(UIUtils.makeV1Observed())`拉齐双方的观察能力。
189
190```ts
191// 推荐
192@Local unObservedClass: UnObservedClass = UIUtils.enableV2Compatibility(UIUtils.makeV1Observed(new UnObservedClass()));
193
194// 推荐,ObservedClass时@Observed装饰的class
195@Local observedClass: ObservedClass = UIUtils.enableV2Compatibility(new ObservedClass());
196```
197- `UIUtils.enableV2Compatibility(UIUtils.makeV1Observed())`不会改变V1和V2本身观察能力。
198    - 在V1中,`UIUtils.enableV2Compatibility(UIUtils.makeV1Observed())`等于V1的观察能力,观察数据本身的赋值和第一层属性的赋值,无法深度观察,如果需要深度观察,则需要配合\@ObjectLink。
199    - 在V2中,`UIUtils.enableV2Compatibility(UIUtils.makeV1Observed())`可以深度观察,但是需要每一层都是\@Observed装饰的class,或者是`makeV1Observed`的返回值。
200- 当数据已使能V2观察能力,即调用`UIUtils.enableV2Compatibility`后,会将新的数据默认使能V2观察能力,但需要开发者保证新增数据也是\@Observed装饰的class,或者是`makeV1Observed`的返回值。完整例子可见[常见场景](#嵌套类型)。
201```ts
202let arr: Array<ArrayItem> = UIUtils.enableV2Compatibility(UIUtils.makeV1Observed(new ArrayItem()));
203
204arr.push(new ArrayItem()); // 新增数据不是V1状态变量,所以不会具有V2观察能力
205arr.push(UIUtils.makeV1Observed(new ArrayItem())); // 新增数据是V1的状态变量,默认在V2中可观察
206```
207- 对于built-in类型,如Array、Map、Set和Date,V1和V2都可以观察自身赋值和其API的调用所带来的变化。虽然开发者在不调用`UIUtils.enableV2Compatibility`时,也可以在一些简单场景下实现数据刷新,但是会带来双重代理导致性能较差的问题,所以推荐开发者使用`UIUtils.enableV2Compatibility(UIUtils.makeV1Observed())`,具体例子见[常见场景](#内置类型)。
208- 对于有\@Track装饰属性的类,非\@Track装饰的属性在\@ComponentV2中使用不会崩溃,在\@Component中使用仍会崩溃。具体例子见[常见场景](#observed装饰的class)。
209
210开发者在使用这两个接口混用V1V2时,可遵循下图逻辑。
211
212![mix-usage](./figures/V1V2_mix_usage.png)
213
214
215## 常见场景
216### 普通JS Object
217
218**V1->V2**
219
220**推荐写法**
221
222```ts
223import { UIUtils } from '@kit.ArkUI';
224
225@Observed
226class ObservedClass {
227  name: string = 'Tom';
228}
229
230@Entry
231@Component
232struct CompV1 {
233  @State observedClass: ObservedClass = new ObservedClass();
234
235  build() {
236    Column() {
237      Text(`@State observedClass: ${this.observedClass.name}`)
238        .onClick(() => {
239          this.observedClass.name += '!'; // 刷新
240        })
241      // 调用UIUtils.enableV2Compatibility使V1的状态变量可在@ComponentV2中有观察能力。
242      CompV2({ observedClass: UIUtils.enableV2Compatibility(this.observedClass) })
243    }
244  }
245}
246
247@ComponentV2
248struct CompV2 {
249  @Param observedClass: ObservedClass = new ObservedClass();
250
251  build() {
252    // V1状态变量在使能V2观察能力后,可以在V2观察第一层的变化
253    Text(`@Param observedClass: ${this.observedClass.name}`)
254      .onClick(() => {
255        this.observedClass.name += '!'; // 刷新
256      })
257  }
258}
259```
260**不推荐写法**
261
262在下面的例子中,V1的状态变量在传递给V2时,未调用`enableV2Compatibility`接口,未使能V2的观察能力,则`observedClass`在CompV2中无法观察属性`name`的变化。同一个状态变量在`CompV1`和`CompV2`中观察能力不一致。
263
264```ts
265@Observed
266class ObservedClass {
267  name: string = 'Tom';
268}
269
270@Entry
271@Component
272struct CompV1 {
273  @State observedClass: ObservedClass = new ObservedClass();
274
275  build() {
276    Column() {
277      Text(`@State observedClass: ${this.observedClass.name}`)
278        .onClick(() => {
279          this.observedClass.name += '!'; // 刷新
280        })
281      // 未调用enableV2Compatibility接口,V1的状态变量在CompV2中无观察能力
282      // 在CompV2不可观察name的变化
283      CompV2({ observedClass: this.observedClass })
284    }
285  }
286}
287
288@ComponentV2
289struct CompV2 {
290  @Param observedClass: ObservedClass = new ObservedClass();
291
292  build() {
293    Text(`@Param observedClass: ${this.observedClass.name}`)
294      .onClick(() => {
295        this.observedClass.name += '!'; // 不刷新
296      })
297  }
298}
299```
300**V2->V1**
301
302**推荐写法**
303
304在V2->V1传递的场景中,为了拉齐V2和V1的观察能力,需要在V2中调用makeV1Observed接口,同时也需要使能V2的观察能力,调用enableV2Compatibility接口,所以推荐写法如下。
305
306```ts
307import { UIUtils } from '@kit.ArkUI';
308
309class ObservedClass {
310  name: string = 'Tom';
311}
312
313@Entry
314@ComponentV2
315struct CompV2 {
316  @Local observedClass: ObservedClass = UIUtils.enableV2Compatibility(UIUtils.makeV1Observed(new ObservedClass()));
317
318  build() {
319    Column() {
320      // @Local原本能力仅可观察自身
321      // 但是调用了UIUtils.makeV1Observed使其变成V1的状态变量,V1状态变量可观察第一层变化
322      // 又调用UIUtils.enableV2Compatibility使其在V2中可观察,使其在V2中可观察
323      // 所以当前可观察第一层属性的变化
324      Text(`@Local observedClass: ${this.observedClass.name}`)
325        .onClick(() => {
326          this.observedClass.name += '!'; // 刷新
327        })
328      // @ObjectLink可接收@Observed装饰class的实例或者makeV1Observed的返回值
329      CompV1({ observedClass: this.observedClass })
330    }
331  }
332}
333
334@Component
335struct CompV1 {
336  @ObjectLink observedClass: ObservedClass;
337
338  build() {
339    // 在CompV1中可观察第一层的变化
340    Text(`@ObjectLink observedClass: ${this.observedClass.name}`)
341      .onClick(() => {
342        this.observedClass.name += '!'; // 刷新
343      })
344  }
345}
346```
347**不推荐写法**
348
349因为V1和V2观察能力不同,如果不调用`UIUtils.enableV2Compatibility(UIUtils.makeV1Observed())`直接进行数据传递,则会造成不刷新或者刷新行为不一致的问题。
350
351```ts
352class ObservedClass {
353  name: string = 'Tom';
354}
355
356@Entry
357@ComponentV2
358struct CompV2 {
359  @Local observedClass: ObservedClass = new ObservedClass();
360
361  build() {
362    Column() {
363      // @Local原本能力仅可观察自身,此处不可观察属性的变化
364      Text(`@Local observedClass: ${this.observedClass.name}`)
365        .onClick(() => {
366          this.observedClass.name += '!'; // 不刷新
367        })
368      // @ObjectLink不可接收非@Observed装饰class的实例或者makeV1Observed的返回值
369      // 日志提示开发者当前ObjectLink被不合法赋值
370      CompV1({ observedClass1: this.observedClass, observedClass2: this.observedClass })
371    }
372  }
373}
374
375@Component
376struct CompV1 {
377  @ObjectLink observedClass1: ObservedClass;
378  @State observedClass2: ObservedClass = new ObservedClass();
379
380  build() {
381    Column() {
382      // @ObjectLink被不合法赋值,不会响应UI刷新
383      Text(`@ObjectLink observedClass: ${this.observedClass1.name}`)
384        .onClick(() => {
385          this.observedClass1.name += '!'; // 不刷新
386        })
387
388      // 不同于@ObjectLink,@State会默认将不可观察的对象包装成V1可观察的对象,可观察到自身和属性的变化
389      Text(`@State observedClass: ${this.observedClass2.name}`)
390        .onClick(() => {
391          this.observedClass2.name += '!'; // 刷新
392        })
393    }
394  }
395}
396```
397### \@Observed装饰的class
398
399**V1->V2**
400
401下面的例子中:
402- `ObservedClass`是\@Observed装饰的class,并在传递给V2时使能了在V2中观察的能力。
403- `name`是`@Track`装饰的属性,其在V1和V2均是可观察的。
404- `count`是非`@Track`装饰的属性,其在V1和V2的UI中使用均是非法的。
405    - 在V1中,如果将非`@Track`装饰的属性使用在UI中,是非法行为,会有运行时报错。
406    - 在V2中,非`@Track`装饰的属性使用在UI不会有运行时报错,但不会响应更新。
407
408```ts
409import { UIUtils } from '@kit.ArkUI';
410
411@Observed
412class ObservedClass {
413  @Track name: string = 'a';
414  count: number = 0;
415}
416
417@Entry
418@Component
419struct CompV1 {
420  @State observedClass: ObservedClass = new ObservedClass();
421  build() {
422    Column() {
423      Text(`name: ${this.observedClass.name}`).onClick(() => {
424        // 触发刷新
425        this.observedClass.name += 'a';
426      })
427      // 使用非@Track的变量在V1中会崩溃
428      // Text(`count: ${this.observedClass.count}`)
429
430      CompV2({ observedClass: UIUtils.enableV2Compatibility(this.observedClass) })
431    }
432  }
433}
434
435@ComponentV2
436struct CompV2 {
437  @Param observedClass: ObservedClass = new ObservedClass();
438  build() {
439    // 使用非@Track的变量在V2中不会崩溃,但不会响应更新
440    Text(`count: ${this.observedClass.count}`).onClick(() => {
441      // 不触发刷新
442      this.observedClass.count++;
443    })
444  }
445}
446```
447**V2->V1**
448
449- `ObservedClass`是\@Observed装饰的class,所以传递给V1调用`UIUtils.enableV2Compatibility`时,无需再调用`UIUtils.makeV1Observed`。
450- 只有\@Track装饰的变量在V1和V2中可观察。非\@Track的变量在V1中使用在UI上会有运行时报错,在V2中不会报错,但不会响应刷新。
451```ts
452import { UIUtils } from '@kit.ArkUI';
453
454@Observed
455class ObservedClass {
456  @Track name: string = 'a';
457  count: number = 0;
458}
459
460@Entry
461@ComponentV2
462struct CompV1 {
463  @Local observedClass: ObservedClass = UIUtils.enableV2Compatibility(new ObservedClass());
464
465  build() {
466    Column() {
467      Text(`name: ${this.observedClass.name}`).onClick(() => {
468        // 触发刷新
469        this.observedClass.name += 'a';
470      })
471      // 使用非@Track的变量在V2中不会崩溃,但不触发刷新
472      Text(`count: ${this.observedClass.count}`).onClick(() => {
473        this.observedClass.count++;
474      })
475
476      CompV2({ observedClass: this.observedClass })
477    }
478  }
479}
480
481@Component
482struct CompV2 {
483  @ObjectLink observedClass: ObservedClass;
484
485  build() {
486    Column() {
487      Text(`count: ${this.observedClass.name}`).onClick(() => {
488        // 触发刷新
489        this.observedClass.name += 'a';
490      })
491      // 使用非@Track的变量在V1中会崩溃
492      // Text(`count: ${this.observedClass.count}`)
493    }
494  }
495}
496```
497
498### 内置类型
499以Array为例。
500
501**V1->V2**
502
503**推荐写法**
504
505```ts
506import { UIUtils } from '@kit.ArkUI';
507
508@Entry
509@Component
510struct ArrayCompV1 {
511  @State arr: Array<number> = UIUtils.makeV1Observed([1, 2, 3]);
512
513  build() {
514    Column() {
515      Text(`V1 ${this.arr[0]}`).onClick(() => {
516        // 点击触发ArrayCompV1和ArrayCompV2变化
517        this.arr[0]++;
518      })
519      // 传递给V2时,发现当前代理是makeV1Observed包装的,且使能V2观察能力
520      // 在ArrayCompV2中Param不会再次包装代理,避免双重代理的问题
521      ArrayCompV2({ arr: UIUtils.enableV2Compatibility(this.arr) })
522    }
523    .height('100%')
524    .width('100%')
525  }
526}
527
528@ComponentV2
529struct ArrayCompV2 {
530  @Param arr: Array<number> = [1, 2, 3];
531
532  build() {
533    Column() {
534      Text(`V2 ${this.arr[0]}`).onClick(() => {
535        // 点击触发ArrayCompV1和ArrayCompV2变化
536        this.arr[0]++;
537      })
538    }
539  }
540}
541```
542**不推荐写法**
543
544在下面的例子中,没有调用enableV2Compatibility和makeV1Observed,则有V1和V2双重代理的问题。
545```ts
546@Entry
547@Component
548struct ArrayCompV1 {
549  @State arr: Array<number> = [1, 2, 3];
550
551  build() {
552    Column() {
553      Text(`V1 ${this.arr[0]}`).onClick(() => {
554        // V1代理,可触发ArrayCompV1的刷新并通知ArrayCompV2更新@Param的值
555        this.arr[0]++;
556      })
557      // 传递给ArrayCompV2,被再次包装V2的代理
558      ArrayCompV2({ arr: this.arr })
559    }
560    .height('100%')
561    .width('100%')
562  }
563}
564
565@ComponentV2
566struct ArrayCompV2 {
567  @Param arr: Array<number> = [1, 2, 3];
568
569  build() {
570    Column() {
571      Text(`V2 ${this.arr[0]}`).onClick(() => {
572        // V1V2双重代理,可触发ArrayCompV1,也可触发ArrayCompV2的刷新
573        this.arr[0]++;
574      })
575    }
576  }
577}
578```
579**V2->V1**
580
581**推荐写法**
582
583```ts
584import { UIUtils } from '@kit.ArkUI';
585
586@Entry
587@ComponentV2
588struct ArrayCompV2 {
589  @Local arr: Array<number> = UIUtils.enableV2Compatibility(UIUtils.makeV1Observed([1, 2, 3]));
590
591  build() {
592    Column() {
593      Text(`V2 ${this.arr[0]}`).fontSize(20).onClick(() => {
594        // 点击触发V2变化,且同步给V1 @ObjectLink
595        this.arr[0]++;
596      })
597      ArrayCompV1({ arr: this.arr })
598    }
599    .height('100%')
600    .width('100%')
601  }
602}
603
604@Component
605struct ArrayCompV1 {
606  @ObjectLink arr: Array<number>;
607
608  build() {
609    Column() {
610      Text(`V1 ${this.arr[0]}`).fontSize(20).onClick(() => {
611        // 点击触发V1变化,且双向同步回给V2
612        this.arr[0]++;
613      })
614    }
615  }
616}
617
618```
619**不推荐写法**
620
621在下面的例子中,没有调用enableV2Compatibility和makeV1Observed,且对\@ObjectLink非法初始化,使其无法观察属性的变化。
622但因为传递给\@ObjectLink是V2的状态变量,所以可以触发V2的刷新。
623```ts
624@Entry
625@ComponentV2
626struct ArrayCompV2 {
627  @Local arr: Array<number> = [1, 2, 3];
628
629  build() {
630    Column() {
631      Text(`V2 ${this.arr[0]}`).fontSize(20).onClick(() => {
632        // 点击触发V2变化
633        this.arr[0]++;
634      })
635      // 传递给@ObjectLink为非@Observed和makeV1Observed数据
636      // 非法操作,@ObjectLink将不能观察属性变化
637      ArrayCompV1({ arr: this.arr })
638    }
639    .height('100%')
640    .width('100%')
641  }
642}
643
644@Component
645struct ArrayCompV1 {
646  @ObjectLink arr: Array<number>;
647
648  build() {
649    Column() {
650      Text(`V1 ${this.arr[0]}`).fontSize(20).onClick(() => {
651        // V1不刷新,但可以触发V2的刷新
652        this.arr[0]++;
653      })
654    }
655  }
656}
657```
658### 二维数组
659
660**V1->V2**
661
662下面的例子中:
663- 使用makeV1Observed将二维数组的内层数组变成V1的状态变量。
664- 在传递给V2子组件时,调用enableV2Compatibility,使其具有V2的观察能力,也避免V1V2的双重代理。
665
666```ts
667import { UIUtils } from '@kit.ArkUI';
668
669@ComponentV2
670struct Item {
671  @Require @Param itemArr: Array<string>;
672
673  build() {
674    Row() {
675      ForEach(this.itemArr, (item: string, index: number) => {
676        Text(`${index}: ${item}`)
677      }, (item: string) => item + Math.random())
678
679      Button('@Param push')
680        .onClick(() => {
681          this.itemArr.push('Param');
682        })
683    }
684  }
685}
686
687@Entry
688@Component
689struct IndexPage {
690  @State arr: Array<Array<string>> =
691    [UIUtils.makeV1Observed(['apple']), UIUtils.makeV1Observed(['banana']), UIUtils.makeV1Observed(['orange'])];
692
693  build() {
694    Column() {
695      ForEach(this.arr, (itemArr: Array<string>) => {
696        Item({ itemArr: UIUtils.enableV2Compatibility(itemArr) })
697      }, (itemArr: Array<string>) => JSON.stringify(itemArr) + Math.random())
698      Divider()
699      Button('@State push two-dimensional array item')
700        .onClick(() => {
701          this.arr[0].push('strawberry');
702        })
703
704      Button('@State push array item')
705        .onClick(() => {
706          this.arr.push(UIUtils.makeV1Observed(['pear']));
707        })
708
709      Button('@State change two-dimensional array first item')
710        .onClick(() => {
711          this.arr[0][0] = 'APPLE';
712        })
713
714      Button('@State change array first item')
715        .onClick(() => {
716          this.arr[0] = UIUtils.makeV1Observed(['watermelon']);
717        })
718    }
719  }
720}
721```
722
723**V2->V1**
724
725下面的例子中:
726- 使用makeV1Observed将二维数组的内层数组变成V1的状态变量。调用enableV2Compatibility,使其具有V2的观察能力,也避免V1V2的双重代理。
727- 在V1中,使用\@ObjectLink接收二维数组的内层数组,因为其为makeV1Observed的返回值,所以点击`Button('@ObjectLink push')`,会正常响应刷新。
728
729```ts
730import { UIUtils } from '@kit.ArkUI';
731
732@Component
733struct Item {
734  @ObjectLink itemArr: Array<string>;
735
736  build() {
737    Row() {
738      ForEach(this.itemArr, (item: string, index: number) => {
739        Text(`${index}: ${item}`)
740      }, (item: string) => item + Math.random())
741
742      Button('@ObjectLink push')
743        .onClick(() => {
744          this.itemArr.push('ObjectLink');
745        })
746    }
747  }
748}
749
750@Entry
751@ComponentV2
752struct IndexPage {
753  @Local arr: Array<Array<string>> =
754    UIUtils.enableV2Compatibility(UIUtils.makeV1Observed([UIUtils.makeV1Observed(['apple']),
755      UIUtils.makeV1Observed(['banana']), UIUtils.makeV1Observed(['orange'])]));
756
757  build() {
758    Column() {
759      ForEach(this.arr, (itemArr: Array<string>) => {
760        Item({ itemArr: itemArr })
761      }, (itemArr: Array<string>) => JSON.stringify(itemArr) + Math.random())
762      Divider()
763      Button('@Local push two-dimensional array item')
764        .onClick(() => {
765          this.arr[0].push('strawberry');
766        })
767
768      Button('@Local push array item')
769        .onClick(() => {
770          this.arr.push(UIUtils.makeV1Observed(['pear']));
771        })
772
773      Button('@Local change two-dimensional array first item')
774        .onClick(() => {
775          this.arr[0][0] = 'APPLE';
776        })
777
778      Button('@Local change array first item')
779        .onClick(() => {
780          this.arr[0] = UIUtils.makeV1Observed(['watermelon']);
781        })
782    }
783  }
784}
785```
786
787### 嵌套类型
788
789**V1->V2**
790
791结合上面的基本场景后,来看下面嵌套场景的例子。
792下面的例子的行为可以总结为:
793- \@State仅能观察第一层的变化,如果要深度观察,需要传递给\@ObjectLink。
794- 数据源\@State的第二层的改变,虽然不能带来本层的刷新,但会被\@ObjectLink和\@Param观察到,并触发它们关联组件的刷新。
795- \@ObjectLink和\@Param是同一个对象的引用,其属性改变也会带来其他引用的刷新。
796- 开启`enableV2Compatibility`后,V2有了深度观察能力。
797- 如果开发者在传递给V2时没有调用`enableV2Compatibility`,则Param无法观察对象的属性。
798```ts
799// 不推荐写法
800NestedClassV2({ outer: this.outer })
801```
802完整例子如下。
803```ts
804import { UIUtils } from '@kit.ArkUI';
805
806class ArrayItem {
807  value: number = 0;
808
809  constructor(value: number) {
810    this.value = value;
811  }
812}
813
814class Inner {
815  innerValue: string = 'inner';
816  arr: Array<ArrayItem>;
817
818  constructor(arr: Array<ArrayItem>) {
819    this.arr = arr;
820  }
821}
822
823class Outer {
824  @Track outerValue: string = 'out';
825  @Track inner: Inner;
826
827  constructor(inner: Inner) {
828    this.inner = inner;
829  }
830}
831
832@Entry
833@Component
834struct NestedClassV1 {
835  // 需保证每一层都是V1的状态变量
836  @State outer: Outer =
837    UIUtils.makeV1Observed(new Outer(
838      UIUtils.makeV1Observed(new Inner(UIUtils.makeV1Observed([
839        UIUtils.makeV1Observed(new ArrayItem(1)),
840        UIUtils.makeV1Observed(new ArrayItem(2))
841      ])))
842    ));
843
844  build() {
845    Column() {
846      Text(`@State outer.outerValue can update ${this.outer.outerValue}`)
847        .fontSize(20)
848        .onClick(() => {
849          // @State可以观察第一层的变化
850          // 变化会通知@ObjectLink和@Param刷新
851          this.outer.outerValue += '!';
852        })
853
854      Text(`@State outer.inner.innerValue cannot update ${this.outer.inner.innerValue}`)
855        .fontSize(20)
856        .onClick(() => {
857          // @State无法观察第二层的变化
858          // 但该变化会被@ObjectLink和@Param观察
859          this.outer.inner.innerValue += '!';
860        })
861      // 将inner传递给@ObjectLink可观察inner属性的变化
862      NestedClassV1ObjectLink({ inner: this.outer.inner })
863      // 将开启enableV2Compatibility的数据传给V2
864      NestedClassV2({ outer: UIUtils.enableV2Compatibility(this.outer) })
865    }
866    .height('100%')
867    .width('100%')
868  }
869}
870
871@Component
872struct NestedClassV1ObjectLink {
873  @ObjectLink inner: Inner;
874
875  build() {
876    Text(`@ObjectLink inner.innerValue can update ${this.inner.innerValue}`)
877      .fontSize(20)
878      .onClick(() => {
879        // 可以触发刷新,和@Param是同一个对象的引用,@Param也会进行刷新
880        this.inner.innerValue += '!';
881      })
882  }
883}
884
885@ComponentV2
886struct NestedClassV2 {
887  @Require @Param outer: Outer;
888
889  build() {
890    Column() {
891      Text(`@Param outer.outerValue can update ${this.outer.outerValue}`)
892        .fontSize(20)
893        .onClick(() => {
894          // 可以观察第一层的变化
895          this.outer.outerValue += '!';
896        })
897      Text(`@Param outer.inner.innerValue can update ${this.outer.inner.innerValue}`)
898        .fontSize(20)
899        .onClick(() => {
900          // 可以观察第二层的变化,和@ObjectLink是同一个对象的引用,也会触发刷新
901          this.outer.inner.innerValue += '!';
902        })
903
904      Repeat(this.outer.inner.arr)
905        .each((item: RepeatItem<ArrayItem>) => {
906          Text(`@Param outer.inner.arr index: ${item.index} item: ${item.item.value}`)
907        })
908
909      Button('@Param push').onClick(() => {
910        // outer已经使能了V2观察能力,对于新增加的数据,则默认开启V2观察能力
911        this.outer.inner.arr.push(UIUtils.makeV1Observed(new ArrayItem(20)));
912      })
913
914      Button('@Param change the last Item').onClick(() => {
915        // 可以观察最后一个数组项的属性变化
916        this.outer.inner.arr[this.outer.inner.arr.length - 1].value++;
917      })
918    }
919  }
920}
921```
922
923**V2->V1**
924
925- 下面的例子中,`NestedClassV2`中`outer`调用了`UIUtils.enableV2Compatibility`,且每一层都是`UIUtils.makeV1Observed`,所以`outer`在V2中有了深度观察的能力。
926- V1中仅能观察第一层的变化,所以需要多层自定义组件,且每层都配合使用\@ObjectLink来接收,从而实现深度观察能力。
927
928```ts
929import { UIUtils } from '@kit.ArkUI';
930
931class ArrayItem {
932  value: number = 0;
933
934  constructor(value: number) {
935    this.value = value;
936  }
937}
938
939class Inner {
940  innerValue: string = 'inner';
941  arr: Array<ArrayItem>;
942
943  constructor(arr: Array<ArrayItem>) {
944    this.arr = arr;
945  }
946}
947
948class Outer {
949  @Track outerValue: string = 'out';
950  @Track inner: Inner;
951
952  constructor(inner: Inner) {
953    this.inner = inner;
954  }
955}
956
957@Entry
958@ComponentV2
959struct NestedClassV2 {
960  // 需保证每一层都是V1的状态变量
961  @Local outer: Outer = UIUtils.enableV2Compatibility(
962    UIUtils.makeV1Observed(new Outer(
963      UIUtils.makeV1Observed(new Inner(UIUtils.makeV1Observed([
964        UIUtils.makeV1Observed(new ArrayItem(1)),
965        UIUtils.makeV1Observed(new ArrayItem(2))
966      ])))
967    )));
968
969  build() {
970    Column() {
971      Text(`@Local outer.outerValue can update ${this.outer.outerValue}`)
972        .fontSize(20)
973        .onClick(() => {
974          // 可观察第一层的变化
975          this.outer.outerValue += '!';
976        })
977
978      Text(`@Local outer.inner.innerValue can update ${this.outer.inner.innerValue}`)
979        .fontSize(20)
980        .onClick(() => {
981          // 可观察第二层的变化
982          this.outer.inner.innerValue += '!';
983        })
984      // 将inner传递给@ObjectLink可观察inner属性的变化
985      NestedClassV1ObjectLink({ inner: this.outer.inner })
986    }
987    .height('100%')
988    .width('100%')
989  }
990}
991
992@Component
993struct NestedClassV1ObjectLink {
994  @ObjectLink inner: Inner;
995
996  build() {
997    Column() {
998      Text(`@ObjectLink inner.innerValue can update ${this.inner.innerValue}`)
999        .fontSize(20)
1000        .onClick(() => {
1001          // 可以触发刷新
1002          this.inner.innerValue += '!';
1003        })
1004      NestedClassV1ObjectLinkArray({ arr: this.inner.arr })
1005    }
1006  }
1007}
1008
1009@Component
1010struct NestedClassV1ObjectLinkArray {
1011  @ObjectLink arr: Array<ArrayItem>;
1012
1013  build() {
1014    Column() {
1015      ForEach(this.arr, (item: ArrayItem) => {
1016        NestedClassV1ObjectLinkArrayItem({ item: item })
1017      }, (item: ArrayItem, index: number) => {
1018        return item.value.toString() + index.toString();
1019      })
1020
1021      Button('@ObjectLink push').onClick(() => {
1022        this.arr.push(UIUtils.makeV1Observed(new ArrayItem(20)));
1023      })
1024
1025      Button('@ObjectLink change the last Item').onClick(() => {
1026        // 在NestedClassV1ObjectLinkArrayItem中可以观察最后一个数组项的属性变化
1027        this.arr[this.arr.length - 1].value++;
1028      })
1029    }
1030  }
1031}
1032
1033@Component
1034struct NestedClassV1ObjectLinkArrayItem {
1035  @ObjectLink item: ArrayItem;
1036
1037  build() {
1038    Text(`@ObjectLink outer.inner.arr item: ${this.item.value}`)
1039  }
1040}
1041
1042```