【ArkUI 入门练中学】第19课:状态管理 V2 深度解析与迁移

发布时间:2026/10/10 9:04:46
【ArkUI 入门练中学】第19课:状态管理 V2 深度解析与迁移
本节目标· 理解状态管理 V2 的设计理念掌握其相比 V1 在深度观测与精准刷新上的核心改进· 掌握 ObservedV2 与 Trace 的配合机制能够实现对嵌套对象的自动深度观测· 掌握 V2 组件状态管理装饰器 Local、Param、Once、Event 的用法与 V1 对应关系· 掌握 Monitor 异步监听与 Computed 计算属性的使用理解 V2 监听能力相比 Watch 的增强· 了解 Provider / Consumer、SyncMonitor、Type、ReusableV2 等 V2 新增装饰器· 掌握 AppStorageV2 与 PersistenceV2 的使用方式理解其与 V1 存储方案的对应关系· 掌握从 V1 到 V2 的迁移策略与步骤理解 V1/V2 混用的限制条件· 能够为已有 V1 项目制定合理的 V2 渐进式迁移方案一、状态管理 V2 概述1.1 为什么需要 V2V1 的核心问题在于观测粒度。V1 使用代理来观测数据当创建状态变量时会同时创建一个数据代理观察者。这个机制在简单场景下工作良好但面对嵌套对象时存在根本性局限State 仅能观察对象的第一层属性变化深层嵌套的属性修改无法被感知。V1 通过 Observed ObjectLink 的组合解决了这个问题但代价是需要逐层创建自定义组件每一层都配上 ObjectLink代码复杂度随嵌套深度线性增长。当嵌套层级较深时代码会变得十分复杂易用性差。V2 对状态管理能力进行了全面升级实现了对数据对象的深度观察与管理能力。V2 通过 ObservedV2 和 Trace 装饰器支持对嵌套类对象属性的直接观测无需像 V1 那样依赖 ObjectLink 和自定义组件封装能够直接深度观测无需中间层。1.2 核心理念属性级精准刷新V2 的观测能力实现在类属性上而非组件层级上。V1 的 ObjectLink 观察的是“被 Observed 装饰的类实例”的属性变化而 V2 的 ObservedV2 Trace 将观测能力直接实现在类属性上——当类属性被 Trace 标记时无论嵌套多少层均能观测到变化。这意味着当被 Trace 装饰的属性变化时仅会通知该属性关联的组件进行刷新。这正是 V2 “属性级精准刷新”的核心——不是整个对象刷新不是整个组件刷新而是精确到使用了该属性的 UI 片段刷新。二、ObservedV2 与 Trace深度观测2.1 基本用法ObservedV2 是类装饰器用于状态管理 V2 中。ObservedV2 与 Trace 配套使用装饰类以及类中的属性使得被装饰的类和属性具有深度观测的能力。从 API version 12 开始支持。// 使用ObservedV2类装饰器使 Son 类具有深度观测能力ObservedV2classSon{// 使用Trace装饰器标记需要观测的属性属性变化时触发 UI 刷新Traceage:number100;}classFather{son:SonnewSon();}EntryComponentV2struct Index{father:FathernewFather();build(){Column(){Text(${this.father.son.age})// 设置点击事件点击后 age 值加 1由于Trace装饰器UI 会自动刷新.onClick((){this.father.son.age;})}}}注意Father 类没有被任何装饰器修饰但 Text 组件中使用了 this.father.son.age——当 age 变化时UI 依然会自动刷新。这是因为 Trace 将观测能力实现在了 Son.age 属性上与 Father 无关与组件层级也无关。2.2 嵌套类的观测规则在嵌套类中嵌套类中的属性被 Trace 装饰且嵌套类被 ObservedV2 装饰时才具有触发 UI 刷新的能力。如果嵌套层级中某一层不是 ObservedV2则该层的属性变化无法被追踪。ObservedV2classAddress{Tracecity:string北京;}// 必须也加ObservedV2否则 address.city 的变化无法被观测ObservedV2classUser{Tracename:string张三;address:AddressnewAddress();}关键规则如果 A 持有 BB 的字段要被观测B 本身必须是 ObservedV2 且字段加 Trace。只给外层加是不够的。路径上的每一层对象都需要是可观察的。2.3 与 V1 Observed / ObjectLink 的对比V1 的 Observed ObjectLink 方案要求嵌套对象逐层拆解自定义组件每一层配上 ObjectLink才能实现深度观测。V2 的 ObservedV2 Trace 则直接深度观测可删除中间层自定义组件代码量大幅减少且刷新粒度更细——属性级精准刷新而非组件级刷新。三、V2 组件状态管理装饰器3.1 ComponentV2ComponentV2 用于创建自定义组件是 V2 组件体系的入口。V1 的自定义组件中不可以使用 V2 的装饰器否则编译报错。当组件间不传递变量时V1 的自定义组件中可以使用 V2 的自定义组件。3.2 Local组件内部状态Local 对应 V1 的 State用于装饰组件内部的状态变量。关键差异· 简单类型可以直接将 State 替换为 Local。· 复杂类型V1 中的 State 可以观察复杂对象的第一层属性变化而 V2 中的 Local 只能观察对象自身的变化。如果需要追踪对象内部的属性变化必须结合 ObservedV2 和 Trace。· Local 禁止外部初始化V1 中 State 支持从外部传递初始值V2 中 Local 不允许。若需要从外部传递初始值使用 Param 和 Once。ComponentV2struct Counter{Localcount:number0;// 简单类型直接替换 Statebuild(){Button(计数${this.count}).onClick((){this.count;})}}3.3 Param组件外部输入Param 对应 V1 的 Prop用于接收父组件传递的数据。Param 是单向输入不建议在子组件中直接修改。ComponentV2struct Child{Paramval:number0;// 接收父组件传递的数据build(){Text(child:${this.val})}}3.4 Once初始化同步一次Once 用于 Param表示该参数只在初始化时从父组件同步一次之后父组件的更新不再同步到子组件。需要持续跟随父组件更新时不要给 Param 加 Once。3.5 Event规范组件输出Event 用于规范组件的输出事件通常与 Param 配合实现父子组件双向同步。在 V1 中 Link 允许父子组件之间双向数据绑定迁移到 V2 时可以用 Param 和 Event 模拟双向同步Param 实现父到子的单向传递子组件再通过 Event 回调函数触发父组件的状态更新。ComponentV2struct Child{Paramval:number0;EventaddOne:()void;// 通过回调通知父组件更新build(){Column(){Text(child:${this.val})Button(1).onClick((){this.addOne();// 通过 Event 回调通知父组件更新})}}}EntryComponentV2struct Parent{LocalmyVal:number0;build(){Column(){Text(parent:${this.myVal})Child({val:this.myVal,addOne:()this.myVal// Param 传递数据Event 传递回调})}}}3.6 双向绑定语法糖V2 提供了 !! 双向绑定语法糖用于简化 Param Event 的双向同步场景。其中 Event 方法名需要声明为 $ Param 属性名。// 子组件ComponentV2struct Child{Paramvalue:number0;Event$value:(val:number)void(){};build(){Button(${this.value}).onClick(()this.$value(this.value1))}}// 父组件中使用 !!Child({value:this.myVal!!})四、V2 新增能力Monitor 与 Computed4.1 Monitor状态变量修改异步监听Monitor 是 V2 的状态变量修改监听装饰器对应 V1 的 Watch但能力更强。关键增强Monitor 不仅能够感知变化后的数据还能获取变化前的数据。V1 的 Watch 只能感知变化后的值而 V2 的 Monitor 提供了 IMonitor 接口可以同时获取旧值和新值。ObservedV2classUserModel{Traceage:number0;Tracename:string;}ComponentV2struct DemoPage{Localuser:UserModelnewUserModel();Monitor(user.age)onAgeChange(monitor:IMonitor):void{// monitor.value() 返回本次变更的信息// 可以获取变化前的值和变化后的值console.info(age changed);}build(){Column(){Text(${this.user.age})Button(add).onClick((){this.user.age;})}}}Monitor 不触发的排查首先确认组件必须是 ComponentV2其次被监听的必须是状态变量、Trace 属性或 Computed最后 ObservedV2 实例要通过 new 创建且变化的字段必须加 Trace。路径监听Monitor 支持监听多个属性需要写清楚路径。监听深层对象时要写完整路径如 Monitor(‘user.address.city’)只写 city 只能监听当前作用域下名为 city 的状态。Monitor 是异步合并触发——一次事件里多个属性连续变化通常只回调一次拿到最终状态。如果需要每次赋值都同步触发使用 SyncMonitor。4.2 Computed计算属性Computed 是 V2 新增的计算属性装饰器。V1 无计算属性相关能力V2 可使用 Computed 避免重复计算。在 Computed 中计算的状态变量被改变时计算属性会重新计算。ObservedV2classPriceModel{Traceprice:number0;Tracecount:number1;Computedgettotal():number{returnthis.price*this.count;}}关键规则Computed 只有依赖的 Local、Param、Trace 等状态变化时才会触发 UI 重新计算和刷新。如果 price / count 没有 Trace打印 total 可能是新值但 UI 不一定会被标脏刷新。使用建议对于简单计算不建议使用计算属性因为计算属性本身也有开销。五、V2 全局状态管理5.1 AppStorageV2AppStorageV2 对应 V1 的 AppStorage是应用级内存状态管理方案。当数据变更时自动通知所有依赖组件更新。import{AppStorageV2}fromkit.ArkUI;ObservedV2exportclassPerson{Tracename:string;Traceage:number0;}// 创建或获取存储的数据constpersonAppStorageV2.connect(Person,key,()newPerson());5.2 PersistenceV2PersistenceV2 对应 V1 的 PersistentStorage是应用级数据持久化技术数据直接存在设备磁盘上退出重新进入后数据还存在。import{PersistenceV2}fromkit.ArkUI;// PersistenceV2 作为 AppStorageV2 的持久化引擎constconfigPersistenceV2.connect(AppConfig,config,()newAppConfig());注意V2 状态管理提供的是 AppStorageV2应用级内存状态和 PersistenceV2应用级持久化状态没有 LocalStorageV2。组件局部状态应使用 Local。六、V1 到 V2 迁移指南6.1 迁移策略对于新开发的应用建议直接采用 V2 版本进行开发。 对于已使用 V1 的应用如果 V1 的功能和性能满足需求无需立即切换至 V2。6.2 迁移步骤推荐的迁移节奏是先把组件装饰器换干净再处理数据类的 ObservedV2 化最后接 AppStorageV2、PersistenceV2 和 Repeat。整体门槛是 API 12。第一步组件装饰器替换将 Component 替换为 ComponentV2State 替换为 LocalProp 替换为 Param。注意 Local 禁止外部初始化Param 是只读的。第二步Link 场景处理V1 的 Link 双向绑定拆成 Param Event子组件发事件让父组件更新。或使用 Provider Consumer 实现跨层级双向同步。第三步数据模型 ObservedV2 化将 Observed 替换为 ObservedV2需要观测的属性加 Trace。嵌套类也要各自加 ObservedV2。第四步存储迁移AppStorage → AppStorageV2PersistentStorage → PersistenceV2。消费侧先迁只读页面再迁写入页面。6.3 V1/V2 混用限制V1 的自定义组件中不可以使用 V2 的装饰器否则编译报错。 当组件间不传递变量时V1 的自定义组件中可以使用 V2 的自定义组件。ObservedV2 和 Observed 嵌套使用时类对象能否被 V1 的装饰器装饰取决于最外层 class 使用的装饰器。如果最外层是 Observed 修饰的类可以和 V1 装饰器一起使用。渐进式迁移桥梁老 V1 组件可用 makeV1Observed 把 Observed 实例包装进 V2 体系做渐进式迁移不必一次性全改。七、多元化习题习题 1判断题题目在状态管理 V2 中Local 装饰的复杂对象可以观察其内部嵌套属性的变化无需额外装饰。答案错误解读V2 中的 Local 只能观察对象自身的变化即引用替换不能观察对象内部属性的变化。如果需要追踪对象内部的属性变化必须结合 ObservedV2 和 Trace。习题 2单选题题目以下哪个 V2 装饰器用于标记类属性为可观察使属性变化时触发关联 UI 刷新A. ObservedV2B. TraceC. MonitorD. Computed答案B解读ObservedV2 标记类为可观察Trace 标记类中的属性为可观察。只有被 Trace 装饰的属性变化才会触发 UI 刷新。Monitor 用于监听状态变量变化Computed 用于计算属性。习题 3多选题题目关于 V1 到 V2 的迁移以下说法正确的有多选A. State 在 V2 中对应 Local但 Local 禁止外部初始化B. Link 在 V2 中拆分为 Param EventC. Watch 在 V2 中对应 MonitorMonitor 能获取变化前的数据D. V1 的自定义组件中可以直接使用 V2 的装饰器答案A、B、C解读State 对应 LocalLocal 禁止外部初始化选项 A 正确。Link 拆分为 Param Event选项 B 正确。Watch 对应 MonitorMonitor 能获取变化前的数据选项 C 正确。V1 的自定义组件中不可以使用 V2 的装饰器否则编译报错选项 D 错误。习题 4代码填空题题目请补全以下代码使 User 类的 name 属性变化时能够触发 UI 刷新。______________classUser{______________ name:string张三;age:number25;}答案ObservedV2、Trace解读ObservedV2 装饰类使其具有深度观测能力Trace 装饰需要被观测的属性。只有被 Trace 装饰的属性变化才会触发 UI 刷新age 没有加 Trace其变化不会触发 UI 刷新。习题 5代码改错题题目以下代码存在错误请指出问题并修正。ComponentV2struct Child{Localcount:number0;// 从父组件接收数据build(){Text(${this.count})}}答案Local 禁止外部初始化不能用于接收父组件传递的数据。修正为使用 ParamComponentV2struct Child{Paramcount:number0;// 接收父组件传递的数据build(){Text(${this.count})}}解读Local 是组件内部状态禁止外部初始化。需要从外部传递初始值时应使用 Param。如果需要该参数只在初始化时同步一次可以配合 Once 使用。习题 6简答题题目简述 V2 的 ObservedV2 Trace 相比 V1 的 Observed ObjectLink 的核心优势。答案V1 的 Observed ObjectLink 方案要求嵌套对象逐层拆解自定义组件每一层配上 ObjectLink代码复杂度随嵌套深度线性增长。V2 的 ObservedV2 Trace 将观测能力实现在类属性上当类属性被 Trace 标记时无论嵌套多少层均能观测到变化。核心优势包括无需逐层创建自定义组件直接深度观测属性级精准刷新仅通知使用了该属性的 UI 片段刷新代码量大幅减少可删除中间层自定义组件。解读V2 的核心改进在于将观测能力从“组件层级”下沉到“类属性层级”。V1 需要组件层级配合才能实现深度观测V2 则直接在数据模型层面解决这正是 V2 被称为“能力升级”的根本原因。习题 7简答题题目简述 V1 到 V2 的迁移步骤以及 V1/V2 混用的核心限制。答案迁移步骤第一步将组件装饰器 Component 替换为 ComponentV2State 替换为 LocalProp 替换为 Param。第二步将 Link 双向绑定拆成 Param Event 或使用 Provider Consumer。第三步将 Observed 替换为 ObservedV2需要观测的属性加 Trace嵌套类也要各自加 ObservedV2。第四步将 AppStorage 迁移到 AppStorageV2PersistentStorage 迁移到 PersistenceV2。混用核心限制V1 的自定义组件中不可以使用 V2 的装饰器否则编译报错。当组件间不传递变量时V1 的自定义组件中可以使用 V2 的自定义组件。渐进式迁移可使用 makeV1Observed 将 Observed 实例包装进 V2 体系。解读迁移的核心原则是“逐步替换、渐进过渡”。V1 和 V2 可以在一个工程中共存但不能在一个组件内混用装饰器。迁移时应按组件逐个替换而非一次性全量改造。八、本节知识点总结V2 设计理念V2 将观测能力实现在类属性层级而非组件层级。ObservedV2 Trace 实现属性级精准刷新无需逐层创建自定义组件代码量大幅减少。核心装饰器映射Component → ComponentV2State → LocalProp → ParamLink → Param EventObserved ObjectLink → ObservedV2 TraceWatch → MonitorProvide / Consume → Provider / ConsumerAppStorage → AppStorageV2PersistentStorage → PersistenceV2。深度观测机制ObservedV2 标记类Trace 标记属性。嵌套类也需要各自加 ObservedV2 且字段加 Trace。只有被 Trace 装饰的属性变化才会触发 UI 刷新。V2 新增能力Monitor 支持获取变化前后的数据异步合并触发。Computed 提供计算属性能力避免重复计算。Once 控制 Param 只初始化同步一次。!! 语法糖简化双向绑定。存储方案AppStorageV2 应用级内存状态PersistenceV2 应用级持久化状态。V2 没有 LocalStorageV2组件局部状态使用 Local。迁移策略新项目直接采用 V2。已有 V1 项目逐步迁移建议按“组件装饰器 → 数据模型 ObservedV2 化 → 存储方案”的顺序进行。V1 组件中不能使用 V2 装饰器。下节预告第20课将进入 ArkUI 综合实战项目的学习从需求分析、架构设计到功能实现与性能优化完整走通一个 HarmonyOS 应用的开发全流程。