写在前面如果你写过鸿蒙 ArkUI 应用大概率遇到过这个场景你写了个用户卡片嵌套对象user { name: Alice, age: 18 }。点按钮改user.name Bob——UI纹丝不动。你查文档发现 V1 的ObservedObjectLink要在子组件套着用父组件改嵌套对象属性根本不触发重绘。你被迫写this.user { ...this.user, name: Bob }整对象重赋值——代码丑、性能差、嵌套一深就崩。你查文档发现鸿蒙 6.1 给了 V2 装饰器ObservedV2Trace——装在 class 上 装在成员上改成员就真触发重绘。你点进去发现 API 一脸懵。这是 V1「重赋值才更新」和 V2「改属性就深层重绘」的分水岭。鸿蒙 6.1 给的答案是ObservedV2Trace——ObservedV2装在 class 上标识可观察、Trace装在成员上追踪变化、改成员就真触发 UI 重绘。本文就用一个真机可跑的「改嵌套对象属性 → UI 重绘」demo把 V2 状态追踪从「听名字一脸懵」讲到「下个项目直接抄」。代码托管在 AtomGit文末有链接真机实拍截图作证。这是 V2 装饰器系列第一篇。适合人群写过鸿蒙应用、被 V1Observed嵌套对象更新折磨过的同学。不适合人群还在学State的同学——出门左转看我的入门篇。一、先讲清楚ObservedV2 Trace 到底是啥一句话ObservedV2 Trace 是鸿蒙 6.1 V2 装饰器给嵌套对象「改属性就深层重绘」的能力。V1 的ObservedObjectLink要套在子组件里用父组件改嵌套对象属性不触发重绘——必须整对象重赋值。V2 的ObservedV2Trace直接装在 class 上 成员上改成员就真触发 UI 重绘不用重赋值。核心 API 一览装饰器作用一句话理解ObservedV2装在 class 上「告诉系统这个类可观察」Trace装在成员上「告诉系统追踪这个成员变化」Local装在 struct 成员上「V2 版 State组件内状态」ComponentV2装在 struct 上「V2 组件启用 V2 装饰器体系」记住这四个往下看。二、动手一个改嵌套对象属性触发重绘的 demo2.1 定义 ObservedV2 class Trace 成员ObservedV2classUserModel{Tracename:stringAliceTraceage:number18}三个细节ObservedV2装在 class 上——标识这个类可观察V2 装饰器体系Trace name/Trace age装在成员上——追踪这两个成员变化class 必须顶层定义不能装在 struct 里V2 装饰器要求2.2 ComponentV2 struct Local 状态EntryComponentV2struct Index{Localuser:UserModelnewUserModel()LocalclickCount:number0LocallogText:string(未操作)build(){// ...}}三个细节ComponentV2装在 struct 上——启用 V2 装饰器体系V1 的Component不能用 V2 装饰器Local user存嵌套对象实例——V2 版的State组件内状态Local clickCount/Local logText存普通状态——Local也能存普通类型2.3 改成员属性 → UI 真重绘Button(改 nameTrace 深层重绘).width(92%).height(44).fontSize(14).onClick((){this.user.nameAlice-${this.clickCount1}// ← 改成员UI 重绘this.clickCountthis.logText第${this.clickCount}次改 nameUI 重绘})这是 V2 最核心的能力——改this.user.name嵌套对象成员就真触发 UI 重绘。V1 要写this.user { ...this.user, name: Bob }整对象重赋值才更新V2 直接改成员就行。2.4 换整个对象也支持Button(换整个 user 对象).width(92%).height(44).fontSize(14).onClick((){this.usernewUserModel()// ← 换整个对象UI 重绘this.clickCountthis.logText第${this.clickCount}次换整个 userUI 重绘})V2 同时支持「改成员」和「换整个对象」两种更新方式——按场景选。三、真机实拍改成员属性真触发 UI 重绘我把这个 demo 装到真机上跑鸿蒙 6.1.1.125, API 24依次点改 name 改 age下面两张都是真机实拍没有任何 P 图。初始态嵌套对象状态nameAlice, age18, clickCount0 三个按钮点改 name 改 age 后nameAlice-2, age19, clickCount3, 日志「第 3 次改 nameUI 重绘」重点看第二张name 从 Alice 变成 Alice-2、age 从 18 变成 19、clickCount 从 0 变成 3——改嵌套对象成员就真触发了 UI 重绘不用整对象重赋值。这是ObservedV2Trace的真机证明。四、V2ObservedV2vs V1Observed啥差异新手最容易纠结的问题既然 V1Observed那么标准鸿蒙为啥要造 V2维度V1ObservedObjectLinkV2ObservedV2Trace装饰器位置class 装Observed子组件装ObjectLinkclass 装ObservedV2成员装Trace改成员属性不触发重绘必须整对象重赋值触发重绘改成员就更新嵌套深度一层多层要套子组件任意层Trace 装到任意成员配套状态State/Prop/LinkLocal/Param/Once组件装饰ComponentComponentV2性能重赋值整对象重改成员就更新轻一句话决策新项目鸿蒙 6.1 用 V2改嵌套对象属性一行搞定V1 项目维护老代码不动。V2 是装饰器体系的未来方向。五、常见坑都是血泪坑症状解法Trace装在 V1Component里编译报「V2 装饰器只能装在 ComponentV2」struct 改ComponentV2class 装在 struct 里编译报「class 不能嵌套定义」class 顶层定义struct 外面ObservedV2忘装改成员不触发重绘class 上必须装ObservedV2Trace忘装改那个成员不重绘每个要追踪的成员都要装TraceLocal用在 V1Component编译报「Local 是 V2 装饰器」struct 改ComponentV2LocalV1 V2 混用编译报「不能多个状态管理装饰器」一个 struct 只用 V1 或只用 V2别混嵌套对象数组改 push不触发重绘数组用Trace 整数组重赋值或用 V2 的Trace数组六、V1/V2 双轨体系鸿蒙 6.1 API 23 装饰器体系 V1/V2 双轨体系组件装饰状态装饰嵌套对象跨层传递V1ComponentState/Prop/LinkObservedObjectLinkProvideConsumeV2ComponentV2Local/Param/OnceObservedV2TraceProviderConsumerV1 和 V2 不能混用——一个 struct 只用 V1 或只用 V2。新项目优先用 V2 避坑。七、完整代码仓库本文所有代码都已托管到AtomGit欢迎 clone、提 issue、点 star仓库地址https://atomgit.com/JaneConan/arkui-observedv2仓库包含完整的「ObservedV2 Trace 改嵌套对象属性触发重绘」demo 工程Index.ets主页面ObservedV2class Trace成员 ComponentV2struct Local状态改成员 / 换整个对象两种更新姿势可直接用 DevEco Studio 打开运行真机装普通应用必能跑八、下一步该学什么跑通这个 demo 之后你的 V2 嵌套对象状态追踪就入门了。这是 V2 装饰器系列第一篇后续按这个顺序往下AppStorageV2下一篇V2 应用级状态全局存取ParamOnceV2 父子传参替代 V1PropProviderConsumerV2 跨层传递替代 V1ProvideConsumeMonitorV2 监听变化替代 V1WatchComputedV2 计算属性自动派生写在最后ObservedV2Trace的本质是**「鸿蒙 6.1 给嵌套对象改属性就深层重绘的能力」**——不是 V1 的「重赋值才更新」是 V2 专门给精细追踪的原生机制。代价是 class 上多装一个ObservedV2、成员上多装一个Trace。一旦你开始用 V2 思维写嵌套对象状态你会发现大部分「用户卡片改 name」「购物车改数量」「表单改字段」的需求都是ObservedV2Trace的自然结果。代码量比 V1 重赋值少一半精细度高一倍。代码已经给你了仓库链接在上面。现在关掉这篇文章打开 DevEco Studio把 demo 跑起来亲手点改 name 改 age 感受下深层重绘。跑通了回来评论区打个「1」我看看有多少人真的动手了。作者JaneConan仓库https://atomgit.com/JaneConan/arkui-observedv2协议Apache-2.0随便用别告我