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 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```