如何解决复杂嵌套对象属性更新失效的问题
===================

#### 问题现象

当页面数据使用的复杂嵌套类型时，涉及的不刷新的原因较多，应该如何排查复杂嵌套对象属性更新失效的问题？

#### 背景知识

*   [@Observed/@ObjectLink](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-observed-and-objectlink)：用于嵌套场景的观察，比如二维数组、对象数组、嵌套类场景，需要观察到深层的属性变化，且@Observed/@ObjectLink需要配套使用。一般的嵌套失效问题可参考官网链接：[常见问题](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-observed-and-objectlink#常见问题)。
*   [makeV1Observed](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-statemanagement#makev1observed19)：将不可观察的对象包装成状态管理V1可观察的对象，其能力等同于@Observed装饰器，可初始化@ObjectLink。

**对于复杂嵌套对象没有触发UI刷新的问题可以从以下方面考虑：**

*   装饰器的作用规则。以@Observed/@ObjectLink为例，根据官方文档[@Observed/@ObjectLink概述](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-observed-and-objectlink#概述)，当一个类能被监听需要满足以下条件：
    1.  被声明的状态变量必须通过new操作符进行创建。
    2.  若这个类是深层嵌套的类，需要保证被监听的属性所在的深层的类也被@Observed装饰。
    3.  在子组件使用@Prop接收状态变量，此时若在子组件中修改该变量，并不会引起父组件刷新，需要使用@ObjectLink接收状态变量，且每接收一层对象都需要单独封装一个@Component子组件，并使用@ObjectLink接收。
*   控制渲染规则。存在[ForEach组件](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-rendering-control-foreach)、[Repeat组件](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-new-rendering-control-repeat)等控制渲染组件时，由于其渲染规则导致的UI不刷新（数据为对象数组时常出现该类问题）。
*   参数传递规则。在进行状态变量传递时，子组件若是采用[@Builder](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-builder)或者[@LocalBuilder](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-localbuilder)装饰的自定义构建函数时，还应该考虑值传递、引用传递与回调传递等参数传递的问题。

#### 解决方案

针对以上排查的各步骤可能会出现的问题，对应的解决方案如下：

<table id="zh-cn_topic_0000002548669803_table926020581843"><tbody><tr id="ZH-CN_TOPIC_0000002655540269__zh-cn_topic_0000002548669803_row02736584419"><td class="cellrowborder" valign="top" width="33.333333333333336%"><p id="ZH-CN_TOPIC_0000002655540269__zh-cn_topic_0000002548669803_p728295818418">场景</p></td><td class="cellrowborder" valign="top" width="33.333333333333336%"><p id="ZH-CN_TOPIC_0000002655540269__zh-cn_topic_0000002548669803_p1028375810415">违反的规则</p></td><td class="cellrowborder" valign="top" width="33.333333333333336%"><p id="ZH-CN_TOPIC_0000002655540269__zh-cn_topic_0000002548669803_p1528314581414">解决方案</p></td></tr><tr id="ZH-CN_TOPIC_0000002655540269__zh-cn_topic_0000002548669803_row1127312584420"><td class="cellrowborder" rowspan="2" valign="top" width="33.333333333333336%"><p id="ZH-CN_TOPIC_0000002655540269__zh-cn_topic_0000002548669803_p122735584410">场景一</p></td><td class="cellrowborder" rowspan="2" valign="top" width="33.333333333333336%"><p id="ZH-CN_TOPIC_0000002655540269__zh-cn_topic_0000002548669803_p1127365817414">违背装饰器作用规则第一点，未使用new创建可深度观测的嵌套对象，导致复杂嵌套对象不刷新。</p></td><td class="cellrowborder" valign="top" width="33.333333333333336%"><p id="ZH-CN_TOPIC_0000002655540269__zh-cn_topic_0000002548669803_p3273195817417">方案一：使用new操作符创建。</p></td></tr><tr id="ZH-CN_TOPIC_0000002655540269__zh-cn_topic_0000002548669803_row527319584419"><td class="cellrowborder" valign="top"><p id="ZH-CN_TOPIC_0000002655540269__zh-cn_topic_0000002548669803_p1827355811411">方案二：使用makeV1Observed方法代替@Observed装饰器。</p></td></tr><tr id="ZH-CN_TOPIC_0000002655540269__zh-cn_topic_0000002548669803_row22738584413"><td class="cellrowborder" valign="top" width="33.333333333333336%"><p id="ZH-CN_TOPIC_0000002655540269__zh-cn_topic_0000002548669803_p182739585412">场景二</p></td><td class="cellrowborder" valign="top" width="33.333333333333336%"><p id="ZH-CN_TOPIC_0000002655540269__zh-cn_topic_0000002548669803_p182736581846">违背装饰器作用规则第二点和第三点，@Observed/@ObjectLink搭配使用不正确，导致复杂嵌套对象不刷新。</p></td><td class="cellrowborder" valign="top" width="33.333333333333336%"><p id="ZH-CN_TOPIC_0000002655540269__zh-cn_topic_0000002548669803_p182731758644">参考官网链接<a href="https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-observed-and-objectlink#复杂嵌套对象属性更改失效" target="_blank">复杂嵌套对象属性更改失效</a>。</p></td></tr><tr id="ZH-CN_TOPIC_0000002655540269__zh-cn_topic_0000002548669803_row182731558248"><td class="cellrowborder" valign="top" width="33.333333333333336%"><p id="ZH-CN_TOPIC_0000002655540269__zh-cn_topic_0000002548669803_p827313581643">场景三</p></td><td class="cellrowborder" valign="top" width="33.333333333333336%"><p id="ZH-CN_TOPIC_0000002655540269__zh-cn_topic_0000002548669803_p2027315818413">违背控制渲染规则，以ForEach为例，参数更新时，key值未改变，导致复杂嵌套对象不刷新。</p></td><td class="cellrowborder" valign="top" width="33.333333333333336%"><p id="ZH-CN_TOPIC_0000002655540269__zh-cn_topic_0000002548669803_p227316581649">详情可参考官网行业常见问题，<a href="https://developer.huawei.com/consumer/cn/doc/harmonyos-faqs/faqs-arkui-1071" target="_blank">如何解决使用@State修饰对象数组，数据变化时页面不刷新问题</a>场景三。</p></td></tr><tr id="ZH-CN_TOPIC_0000002655540269__zh-cn_topic_0000002548669803_row627314581949"><td class="cellrowborder" valign="top" width="33.333333333333336%"><p id="ZH-CN_TOPIC_0000002655540269__zh-cn_topic_0000002548669803_p18273185812419">场景四</p></td><td class="cellrowborder" valign="top" width="33.333333333333336%"><p id="ZH-CN_TOPIC_0000002655540269__zh-cn_topic_0000002548669803_p727315583420">违反参数传递规则，在进行复杂嵌套对象属性更新时，由于内部自定义组件使用了@Builder/@LocalBuilder自定义构建函数，且使用的值传递的方式，导致复杂嵌套对象不刷新。</p></td><td class="cellrowborder" valign="top" width="33.333333333333336%"><p id="ZH-CN_TOPIC_0000002655540269__zh-cn_topic_0000002548669803_p112731558343">在不违反其它上述使用规则的前提下，<a href="https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-builder#按回调传递参数" target="_blank">按回调传递参数</a>、<a href="https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-builder#按引用传递参数" target="_blank">按引用传递参数</a>即可刷新自定义构建函数内的UI。</p></td></tr></tbody></table>

![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/d9/v3/i4ILaWoETsakMM1-GrKivg/notice_3.0-zh-cn.png?HW-CC-KV=V1&HW-CC-Date=20260709T134051Z&HW-CC-Expire=86400&HW-CC-Sign=BE5C8C5203268F6FE054531555019CB0AE749EB579469C9BFC2DC4EADC49E763)

在状态管理V2中深度观测的装饰器为[@ObservedV2/@Trace](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-new-observedv2-and-trace)，其装饰器规则与V1版本@Observed/@ObjectLink存在一定差异，详情可参考官网行业常见问题[使用@ObservedV2和@Trace深度观测对象时，数据改变而UI未刷新](https://developer.huawei.com/consumer/cn/doc/harmonyos-faqs/faqs-arkui-1011)、[使用状态管理V1、V2进行深度观测的区别和优缺点](https://developer.huawei.com/consumer/cn/doc/harmonyos-faqs/faqs-arkui-1068)，其它排查步骤一致。

*   场景一：违背装饰器作用规则第一点，未使用new创建可深度观测的嵌套对象，导致复杂嵌套对象不刷新。
    
    *   方案一：参考官网行业常见问题，[如何解决使用@State修饰对象数组，数据变化时页面不刷新问题](https://developer.huawei.com/consumer/cn/doc/harmonyos-faqs/faqs-arkui-1071)场景一，使用new创建即可。
    *   方案二：状态管理V1中，为了应对某些三方包中的类等无法被状态管理装饰器装饰，从而导致UI无法刷新的问题，在V1版本中推出了makeV1Observed接口，实现普通变量的深层监听。使用方式如下：
        
        @State items: Item\[\] = \[
          UIUtils.makeV1Observed({ name: '张三', num: 18 }),
          UIUtils.makeV1Observed({ name: '李四', num: 20 })
        \];
        
    
    完整的代码参考如下：
    
    import { UIUtils } from '@kit.ArkUI';
    
    
    @Observed
    class Item {
      name: string = '';
      num: number = 0;
    }
    
    
    @Component
    @Entry
    struct SceneOneOptionTwo {
      @State items: Item\[\] = \[
        UIUtils.makeV1Observed({ name: '张三', num: 18 }),
        UIUtils.makeV1Observed({ name: '李四', num: 20 })
      \];
    
    
      build() {
        Column() {
          ForEach(this.items, (item: Item) => {
            MyItem({ item: item });
          });
        };
      }
    }
    
    
    @Component
    struct MyItem {
      @ObjectLink item: Item;
    
    
      build() {
        Row() {
          Text(this.item.name);
          Row() {
            Button('+').onClick(() => {
              this.item.num++;
            });
            Row() {
              Text(\`${this.item.num}\`);
            }
            .width(50)
            .justifyContent(FlexAlign.Center);
    
    
            Button('-').onClick(() => {
              this.item.num--;
            });
          };
        }
        .margin({
          top: 20
        })
        .justifyContent(FlexAlign.SpaceAround)
        .width('100%');
      }
    }
    
    实现效果如下：
    
    ![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/ff/v3/gKf0UgPyRMCFB5ZqpiNP4g/zh-cn_image_0000002658968441.png?HW-CC-KV=V1&HW-CC-Date=20260709T134051Z&HW-CC-Expire=86400&HW-CC-Sign=D5812E919D4882E7C11C28149010FE67D138B93DA77B913214053539D564D794 "点击放大")
    
*   场景二：违背装饰器作用规则第二点和第三点，@Observed/@ObjectLink搭配使用不正确，导致复杂嵌套对象不刷新。
    
    @ObjectLink装饰器仅能观察其代理的属性，无法观察到代理属性的嵌套对象属性，所以每一层被嵌套的对象都需要封装一个子组件并用@ObjectLink接收。详情可参考官网链接：[复杂嵌套对象属性更改失效](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-observed-and-objectlink#复杂嵌套对象属性更改失效)。
    
*   场景三：违背控制渲染规则，以ForEach为例，参数更新时，key值未改变，导致复杂嵌套对象不刷新。
    
    在ForEach组件进行非首次渲染时，它会检查新生成的键值是否在上次渲染中已经存在。如果键值不存在，则会创建一个新的组件；如果键值存在，则不会创建新的组件，而是直接渲染该键值所对应的组件。此时，若未注意控制渲染组件刷新规则，则可能造成UI不刷新的情况，详情可参考官网行业常见问题，[如何解决使用@State修饰对象数组，数据变化时页面不刷新问题](https://developer.huawei.com/consumer/cn/doc/harmonyos-faqs/faqs-arkui-1071)场景三，区别控制渲染刷新与状态管理刷新即可。
    
*   场景四：违反参数传递规则，在进行复杂嵌套对象属性更新时，由于内部自定义组件使用了@Builder/@LocalBuilder自定义构建函数，且使用的值传递的方式，导致复杂嵌套对象不刷新。
    
    在@Builder/@LocalBuilder自定义构建函数传参过程中，如果使用的是值传递的方式，会导致自定义构建函数中的UI不会刷新，建议使用引用传递与回调传递，详情可参考官网：[按回调传递参数](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-builder#按回调传递参数)、[按引用传递参数](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-builder#按引用传递参数)。此时，需要注意深层嵌套的数据需要是被@ObjectLink装饰器接收的，否则依旧不会刷新。完整示例代码如下：
    
    @Observed
    class ItemFour {
      name: string = '';
      num: number = 0;
    
    
      constructor(name: string, num: number) {
        this.name = name;
        this.num = num;
      }
    }
    
    
    interface BuilderItem {
      name: string;
      num: number;
    }
    
    
    @Component
    @Entry
    struct SceneFour {
      @State items: ItemFour\[\] = \[new ItemFour('张三', 18), new ItemFour('李四', 20)\];
    
    
      build() {
        Column() {
          ForEach(this.items, (item: ItemFour) => {
            Column() {
              Text('Builder传递的是@ObjectLink接收的参数(可以刷新)：');
              MyItemFour({ item: item });
              Text('Builder传递的参数未经过@ObjectLink接收（无法刷新）：');
              Row() {
                Text(\`${item.name}\`);
                Row() {
                  Button('+').onClick(() => {
                    item.num++;
                  });
                  builderFunctionTwo({ name: item.name, num: item.num }); _// 引用传递可以刷新_
                  Button('-').onClick(() => {
                    item.num--;
                  });
                };
              }
              .margin({
                top: 20
              })
              .justifyContent(FlexAlign.SpaceAround)
              .width('100%');
            }.width('90%');
          });
        }.width('100%');
      }
    }
    
    
    @Builder
    function builderFunctionTwo(item: BuilderItem) {
      Row() {
        Text(\`${item.num}\`);
      }
      .width(50)
      .justifyContent(FlexAlign.Center);
    }
    
    
    @Builder
    function builderFunction(item: BuilderItem) {
      Row() {
        Text(\`${item.num}\`);
      }
      .width(50)
      .justifyContent(FlexAlign.Center);
    }
    
    
    @Component
    struct MyItemFour {
      @ObjectLink item: ItemFour;
    
    
      build() {
        Row() {
          Text(\`${this.item.name}\`);
          Row() {
            Button('+').onClick(() => {
              this.item.num++;
            });
            builderFunction({ name: this.item.name, num: this.item.num }); _// 引用传递可以刷新_
            Button('-').onClick(() => {
              this.item.num--;
            });
          };
        }
        .margin({
          top: 20
        })
        .justifyContent(FlexAlign.SpaceAround)
        .width('100%');
      }
    }
    
    实现效果如下：
    
    ![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/18/v3/11gSJIKCSPy9K5DtJ14iEg/zh-cn_image_0000002658848489.png?HW-CC-KV=V1&HW-CC-Date=20260709T134051Z&HW-CC-Expire=86400&HW-CC-Sign=3A8DFE7225E45A39D2EF8F89F29A241A5917A167CAA8F414F4274BBA09DA5F7B "点击放大")