目录
1 自定义组件混用场景指导
1.1 概述
1.2 状态管理装饰器总览
状态管理V1的装饰器
状态管理V2的装饰器
状态管理装饰器支持的数据类型总览
1.3 限制条件
1.3.1 V1和V2的装饰器不允许混用
1.V1的自定义组件中不可以使用V2的装饰器
2.V2的自定义组件中不可以使用V1的装饰器
3. 多个装饰器不允许装饰同一个变量(@Watch、@Once、@Require除外)
2 混用场景介绍
2.1 V1和V2类相关装饰器混用
1. V1的自定义组件中使用被@ObservedV2装饰的类对象
2. V2的自定义组件中使用被@Observed装饰的类对象
2.2 不存在变量传递时,V1和V2的自定义组件混用
1. V1中使用V2的自定义组件
2. V2中使用V1的自定义组件
2.3 存在变量传递时,V1和V2的自定义组件数据混用
1. V1->V2:V1的普通变量传递给V2的自定义组件
2. V1->V2:V1的状态变量传递给V2的自定义组件
3. V2->V1:V2的普通变量传递给V1的自定义组件
4. V2->V1:V2的状态变量传递给V1的自定义组件
2.4 混用场景总结
3 V1->V2迁移指导
3.1 概述
3.2 V1V2使用指引
3.3 迁移指南的目的
3.4 V1V2能力对比及迁移简表
3.5 各装饰器迁移示例
@State->@Local
@Link -> @Param/@Event
@Prop -> @Param
@ObjectLink/@Observed/@Track -> @ObservedV2/@Trace
@Provide/@Consume -> @Provider/@Consumer
@Watch -> @Monitor
@Computed
LocalStorage->全局@ObservedV2/@Trace
AppStorage->AppStorageV2
Environment->调用Ability接口直接获取系统环境变量
PersistentStorage->PersistenceV2
1 自定义组件混用场景指导
1.1 概述
状态管理V1与V2的混用规则可以概括为:
-
V1的自定义组件中不可以使用V2的装饰器,否则编译报错。
-
组件间不存在变量传递时,V1的自定义组件中可以使用V2的自定义组件,包括import第三方@ComponentV2装饰的自定义组件。
-
组件间存在变量传递时,V1的变量传递给V2的自定义组件,有如下限制:
- V1中未被装饰器装饰的变量(后称普通变量):V2只能使用@Param接收。
- V1中被装饰器装饰的变量(后称状态变量):V2存在只能通过@Param装饰器接收,且仅限于boolean、number、enum、string、undefined、null这些简单类型数据。
-
V2的自定义组件中不可以使用V1的装饰器,否则编译报错。
-
组件间不存在变量传递时,V2自定义组件可以使用V1的自定义组件,包括import第三方@Component装饰的自定义组件。
-
组件间存在变量传递时,V2的变量传递给V1的自定义组件,有如下限制:
- V2中未被装饰器装饰的变量(后称普通变量):若V1使用装饰器装饰接收的数据,只能通过@State、@Prop、@Provide。
- V2中被装饰器装饰的变量(后称状态变量):若V1使用装饰器装饰接收的数据,不支持内置类型数据:Array、Set、Map、Date。
1.2 状态管理装饰器总览
状态管理V1的装饰器
装饰器类别 | 装饰器 |
---|---|
组件内装饰器 | @State、@Prop、@Link、@ObjectLink、@Provide、@Consume、@StorageProp、@StorageLink、@LocalStorageProp、@LocalStorageLink、@Watch |
类相关装饰器 | @Observed、@Track |
状态管理V2的装饰器
装饰器类别 | 装饰器 |
---|---|
组件内装饰器 | @Local、@Param、@Provider、@Consumer、@Once、@Event、@Monitor、@Computed |
类相关装饰器 | @ObservedV2、@Trace、@Type |
状态管理装饰器支持的数据类型总览
状态管理能够支持的数据类型有:
数据类型 | 关键字 |
---|---|
简单类型数据 | boolean、number、enum、string、null、undefined |
function类型 | function(仅V2的@Event、@Monitor、@Computed支持) |
Object类型 | Object |
Class类型 | Class |
内置类型 | Array、Map、Set、Date |
1.3 限制条件
1.3.1 V1和V2的装饰器不允许混用
1.V1的自定义组件中不可以使用V2的装饰器
V2的组件内装饰器不允许在V1的自定义组件中使用,编译会报错。
@Local、@Param、@Event,@Provider、@Consumer、@Monitor、@Computed和示例代码中的装饰器表现一致。
2.V2的自定义组件中不可以使用V1的装饰器
V1的组件内装饰器不允许在V2的自定义组件中使用,编译会报错。
@ObjectLink、@Provide、@Consume、@StorageProp、@StorageLink、@LocalStorageProp、@LocalStorageLink和示例的装饰器表现一致。
3. 多个装饰器不允许装饰同一个变量(@Watch、@Once、@Require除外)
除了@Watch、@Once、@Require这些能力扩展装饰器可以配合其他装饰器使用外,其他装饰器不允许装饰同一个变量。
2 混用场景介绍
2.1 V1和V2类相关装饰器混用
1. V1的自定义组件中使用被@ObservedV2装饰的类对象
@ObservedV2的使用需要遵循如下规则:
- @ObservedV2只能装饰Class,@Trace、@Type只能装饰类属性,且只能在@ObservedV2中使用。
- @Track不可以在@ObservedV2中使用。
- 对于被@ObservedV2装饰的Class,不可以直接被V1的装饰器装饰,否则编译时报错。
- 示例中,开发者去掉报错的装饰器即可正常运行,被@Trace装饰的类属性变化时可以观察到变化,否则不可以观测到变化。
2. V2的自定义组件中使用被@Observed装饰的类对象
不建议开发者在V2中使用@Observed装饰的Class,因为@Observed和@Track仅能对类属性做区分,无观测能力,使用@Observed和@ObjectLink拆分嵌套数据才能够观测深层次数据,但@ObjectLink无法在V2的自定义组件中使用。
开发者在对V1的代码向V2迁移时,@Observed装饰的Class不建议在@ComponentV2中使用,无观测能力,如果一定要使用,则遵循以下规则:
- @Observed只能装饰Class,且@Trace不可以在@Observed中使用。
- @Observed和@Track无任何观测能力,只能用于防止Class中一个类属性改变而导致整个Class的刷新。
- 继承自@Observed的Class被V2装饰器装饰,V2的组件内装饰器无类属性观测能力,所以使用@Observed会无法观测到类属性变化。
- 示例中,开发者去掉报错的装饰器即可正常运行,由于无观测能力,所以不建议V2中使用@Observed。
2.2 不存在变量传递时,V1和V2的自定义组件混用
1. V1中使用V2的自定义组件
V1中使用V2的自定义组件,当不存在变量传递时无影响,若涉及变量传递,请见下一节V1和V2的数据混用。
2. V2中使用V1的自定义组件
2中使用V1的自定义组件,当不存在变量传递时无影响,若涉及变量传递,请见下一节V1和V2的数据混用。
2.3 存在变量传递时,V1和V2的自定义组件数据混用
1. V1->V2:V1的普通变量传递给V2的自定义组件
当V1的普通变量传递给V2的自定义组件时,有如下限制:
- V2的自定义组件必须通过@Param接收数据。
- 接收数据的观测能力为@Param能力,对于接收的Class,需要通过@ObservedV2和@Trace才能观察变化。
2. V1->V2:V1的状态变量传递给V2的自定义组件
当V1的状态变量给V2的自定义组件时,有如下规则:
仅支持简单类型变量,其余类型数据会在编译时报错。
示例中使用了@State装饰器,@Prop、@Link、@ObjectLink、@Provide、@Consume、@StorageProp、@StorageLink、@LocalStorageProp、@LocalStorageLink行为和@State保持一致。
3. V2->V1:V2的普通变量传递给V1的自定义组件
当V2的普通变量传递给V1自定义组件时:
V1可以不使用装饰器接收数据,接收过来的变量在V1组定义组件内也会是普通变量。
V1若使用装饰器接收数据,仅可通过@State、@Prop、@Provide接收。
4. V2->V1:V2的状态变量传递给V1的自定义组件
V2的状态变量传递给V1的自定义组件,存在如下限制:
V1可以不使用装饰器接收数据,接收过来的变量在V1组定义组件内也会是普通变量。
V1若使用装饰器接收数据,仅可通过@State、@Prop、@Provide接收。
V1若使用装饰器接收数据,不支持内置类型的数据。
2.4 混用场景总结
通过对V1和V2的混用场景详细梳理,可以看到,当V2的代码混用V1的代码时,即V1的组件或者类数据向V2进行传递,大部分V1的能力在V2都是被禁止的。而V1的代码去混用V2代码时,即V2的组件或者类数据向V1传递,做了部分功能开放,例如@ObservedV2和@Trace,这也是对V1嵌套类数据的观测能提供的最大的帮助。所以在代码开发时,不鼓励开发者使用V1和V2进行混用开发,但是对于代码迁移上,可以让V1的开发者逐步将代码向V2进行迁移,从而稳步替换V1的功能代码,并且十分不鼓励开发者在V2的代码架构上混用V1的代码。
3 V1->V2迁移指导
3.1 概述
ArkUI状态管理的主要职责是:负责将可观察数据的变化自动同步到UI界面,实现数据驱动的UI刷新,使开发者能更加够专注于UI界面的实现和设计。
在状态管理框架的演进过程中,先后推出了状态管理V1和V2两个版本。V1强调组件层级的状态管理,而V2则增强了对数据对象的深度观察与管理能力,不再局限于组件层级。通过V2,开发者能够更灵活地控制数据和状态,实现更高效的UI刷新。具体V1和V2的区别可以参见状态管理概述。
3.2 V1V2使用指引
- V2是V1的增强版本,为开发者提供更多功能和灵活性。
- 对于新开发的应用,建议直接使用V2版本范式来进行开发。
- 对于已经使用V1的应用,如果V1的功能和性能已能满足需求,则不必立即切换到V2。如果开发者在开发过程中受限于V1不能深度观察等特性,则建议开发者尽早规划向V2的迁移,以便未来实现平滑过渡和改进。
- 对于需要在现阶段混用V1和V2的场景,请参阅混用文档。编译器、工具链、IDE对某些不推荐的误用和混用场景会进行校验,虽然开发者可能可以通过特殊手段绕过这些校验,但还是强烈建议开发者遵循混用文档的指导,避免因双重代理等问题给应用带来不确定性。
3.3 迁移指南的目的
- 对希望将现有V1应用迁移到V2的开发者,提供系统化的模板和指导,帮助完成V1到V2的迁移。
- 对希望逐步将V1应用过渡到V2的开发者,提供参考,结合本迁移文档与混用文档,可以帮助开发者实现逐步改造。
- 尚未开始开发应用但已熟悉V1状态管理规则的开发者,可以参考本迁移文档及V2各个装饰器和接口的文档,开始使用V2进行应用开发。
3.4 V1V2能力对比及迁移简表
V1装饰器名 | V2装饰器名 | 说明 |
---|---|---|
@Observed | @ObservedV2 | 表明当前对象为可观察对象。但两者能力并不相同。 @Observed可观察第一层的属性,需要搭配@ObjectLink使用才能生效。 @ObservedV2本身无观察能力,仅代表当前class可被观察,如果要观察其属性,需要搭配@Trace使用。 |
@Track | @Trace | V1装饰器@Track为精确观察,不使用则无法做到类属性的精准观察。 V2@Trace装饰的属性可以被精确跟踪观察。 |
@Component | @ComponentV2 | @Component为搭配V1状态变量使用的自定义组件装饰器。 @ComponentV2为搭配V2状态变量使用的自定义组件装饰器。 |
@State | 无外部初始化:@Local 外部初始化一次:@Param@Once | @State和@Local类似都是数据源的概念,在不需要外部传入初始化时,可直接迁移。如果需要外部传入初始化,则可以迁移为@Param@Once,详情见@State->@Local。 |
@Prop | @Param | @Prop和@Param类似都是自定义组件参数的概念。当输入参数为复杂类型时,@Prop为深拷贝,@Param为引用。 |
@Link | @Param@Event | @Link是框架自己封装实现的双向同步,对于V2开发者可以通过@Param@Event自己实现双向同步。 |
@ObjectLink | @Param | 直接兼容,@ObjectLink需要被@Observed装饰的class的实例初始化,@Param没有此限制。 |
@Provide | @Provider | 兼容。 |
@Consume | @Consumer | 兼容。 |
@Watch | @Monitor | @Watch用于监听V1状态变量的变化,具有监听状态变量本身和其第一层属性变化的能力。状态变量可观察到的变化会触发其@Watch监听事件。 @Monitor用于监听V2状态变量的变化,搭配@Trace使用,可有深层监听的能力。状态变量在一次事件中多次变化时,仅会以最终的结果判断是否触发@Monitor监听事件。 |
LocalStorage | 全局@ObservedV2@Trace | 兼容。 |
AppStorage | AppStorageV2 | 兼容。 |
Environment | 调用Ability接口获取系统环境变量 | Environment获取环境变量能力和AppStorage耦合。在V2中可直接调用Ability接口获取系统环境变量。 |
PersistentStorage | PersistenceV2 | PersistentStorage持久化能力和AppStorage耦合,PersistenceV2持久化能力可独立使用。 |
3.5 各装饰器迁移示例
@State->@Local
迁移规则
在V1中,@State装饰器用于装饰组件内部的状态变量,在V2中提供了@Local作为其替代能力,但两者在观察能力和初始化规则上存在明显差异。针对不同的使用场景,迁移策略如下:
- 简单类型:对于简单类型的变量,可以直接将@State替换为@Local。
- 复杂类型:V1中的@State可以观察复杂对象的第一层属性变化,而V2中的@Local只能观察对象自身的变化。如果需要追踪对象内部的属性变化,可以结合使用@ObservedV2和@Trace。
- 外部初始化:V1中,@State支持从外部传递初始值,但在V2中,@Local禁止外部初始化。若需要从外部传递初始值,可以使用@Param和@Once装饰器来实现类似的效果。
@Link -> @Param/@Event
迁移规则
在V1中,@Link允许父组件和子组件之间进行双向数据绑定。迁移到V2时,可以用@Param和@Event模拟双向同步。@Param实现父到子的单向传递,子组件再通过@Event回调函数触发父组件的状态更新。
@Prop -> @Param
迁移规则
在V1中,@Prop装饰器用于从父组件传递参数给子组件,这些参数在子组件中可以被直接修改。在V2中,@Param取代了@Prop的作用,但@Param是只读的,子组件不能直接修改参数的值。因此,根据场景的不同,有几种迁移策略:
- 简单类型:对于简单类型的参数,可以直接将@Prop替换@Param。
- 复杂类型:如果传递的是复杂对象且需要严格的单向数据绑定,可以对对象进行深拷贝,防止子组件修改父组件的数据。
- 子组件修改变量:如果子组件需要修改传入的参数,可以使用@Once来允许子组件对在本地修改该变量。但需要注意,如果使用了@Once,则代表当前子组件只会被初始化一次,后续并没有父组件到子组件的同步能力。
复杂类型的单向数据传递
在V2中,传递复杂类型时,如果希望实现严格的单向数据绑定,防止子组件修改父组件的数据,需要在使用@Param传递复杂对象时进行深拷贝以避免传递对象的引用。
子组件修改变量
在V1中,子组件可以修改@Prop的变量,然而在V2中,@Param是只读的。如果子组件需要修改传入的值,可以使用@Param和@Once允许子组件在本地修改。
@ObjectLink/@Observed/@Track -> @ObservedV2/@Trace
迁移规则
在V1中,@Observed与@ObjectLink装饰器用于观察类对象及其嵌套属性的变化,但V1只能直接观察对象的第一层属性。对于嵌套对象的属性,必须通过自定义组件和@ObjectLink实现观察。此外,V1中提供了@Track装饰器来实现对属性级别变化的精确控制。
在V2中,@ObservedV2与@Trace结合使用,可以高效地实现类对象及其嵌套属性的深度观察,省去了对自定义组件的依赖,简化了开发流程。同时,@Trace装饰器还具备精确更新的能力,替代了V1中的@Track,从而实现更高效的UI刷新控制。根据不同的场景,有以下迁移策略:
- 嵌套对象的属性观察:V1中需要通过自定义组件和@ObjectLink观察嵌套属性,V2中则可以使用@ObservedV2和@Trace直接观察嵌套对象,简化了代码结构。
- 类属性的精确更新:V1中的@Track可以用V2中的@Trace取代,@Trace可以同时观察和精确更新属性变化,使代码更简洁高效。
嵌套对象属性观察方法
在V1中,无法直接观察嵌套对象的属性变化,只能观察到第一层属性的变化。必须通过创建自定义组件并使用@ObjectLink来实现对嵌套属性的观察。V2中使用@ObservedV2和@Trace,可以直接对嵌套对象的属性进行深度观察,减少复杂度。
类属性变化观测
在V1中,@Observed用于观察类实例及其属性的变化,@Track则用于对属性级别的变化优化,使得只有被@Track装饰的属性触发UI更新。在V2中,@Trace结合了观察和更新属性级别变化的能力,搭配@ObservedV2实现高效的UI更新。
@Provide/@Consume -> @Provider/@Consumer
迁移规则
V1的@Provide/@Consume和V2@Provider/@Consumer定位和作用大体类似,基本可以实现丝滑替换,但是有以下细微差距,开发者可根据自己代码实现来参考是否需要调整:
在V1中,@Provide和@Consume用于父子组件之间的数据共享,可以通过alias(别名)或属性名匹配,同时@Consume必须依赖父组件的@Provide,不允许本地初始化。而V2中,@Provider和@Consumer增强了这些特性,使数据共享更加灵活。根据不同的场景,有以下迁移策略:
- V1中@Provide/@Consume在没有指定alias的情况下,可以直接使用。V2中@Provider/@Consumer是标准装饰器,且参数可选,所以不管有无指定alias后面需要必须跟随“()”。
- alias和属性名匹配规则:V1中,@Provide和@Consume可以通过alias或属性名匹配;V2中,alias是唯一的匹配key,指定alias后只能通过alias匹配。
- 本地初始化支持:V1中,@Consume不允许本地初始化,必须依赖父组件;V2中,@Consumer支持本地初始化,当找不到对应的@Provider时使用本地默认值。
- 从父组件初始化:V1中,@Provide可以直接从父组件初始化;V2中,@Provider不支持外部初始化,需用@Param和@Once接受初始值并赋给 @Provider。
- 重载支持:V1中,@Provide默认不支持重载,需设置 allowOverride;V2中,@Provider默认支持重载,@Consumer会向上查找最近的@Provider。
@Watch -> @Monitor
迁移规则
在V1中,@Watch用于监听状态变量的变化,并在变量变化时触发指定回调函数。在V2中,@Monitor替代了@Watch,可以更灵活地监听变量的变化,并获取变量变化前后的值。具体的迁移策略如下:
- 单变量监听:对于简单的场景,可以直接用@Monitor替换@Watch,效果一致。
- 多变量监听:V1的@Watch无法获取变化前的值。在V2中,@Monitor支持同时监听多个变量,并可以访问变量变化前后的状态。
@Computed
迁移规则
V1中并没有提供计算属性的概念,所以对于UI中的冗余计算,并没有办法可以减少重复计算。V2针对该场景,提供了@Computed装饰器,可以帮助开发者减少重复计算。
LocalStorage->全局@ObservedV2/@Trace
迁移规则
LocalStorage的目的是为了实现页面间的状态变量共享。之所以提供这个能力,是因为V1状态变量和View层耦合,无法由开发者自主地实现页面间状态变量的共享。
对于状态管理V2,状态变量的观察能力内嵌到数据本身,不再和View层耦合,所以对于状态管理V2,不再需要类似LocalStorage的能力,可以使用全局@ObservedV2/@Trace,由开发者自己import和export,自己实现状态变量的页面间共享。
AppStorage->AppStorageV2
上一小节中,对于全局的@ObserveV2/@Trace的改造并不适合跨Ability的数据共享,该场景可以使用AppStorageV2来替换。
Environment->调用Ability接口直接获取系统环境变量
V1中,开发者可以通过Environment来获取环境变量,但Environment获取的结果无法直接使用,需要配合AppStorage才能得到对应环境变量的值。
PersistentStorage->PersistenceV2
V1中PersistentStorage提供了持久化UI数据的能力,而V2则提供了更加方便使用的PersistenceV2接口来替代它。
- PersistentStorage持久化的触发时机依赖AppStorage的观察能力,且与AppStorage耦合,开发者无法自主选择写入或读取持久化数据的时机。
- PersistentStorage使用序列化和反序列化,并没有传入类型,所以在持久化后,会丢失其类型,且对象的属性方法不能持久化。
对于PersistenceV2:
- 与PersistenceV2关联的@ObservedV2对象,其@Trace属性的变化,会触发整个关联对象的自动持久化。
- 开发者也可以调用PersistenceV2.save和PersistenceV2.connect接口来手动触发持久化写入和读取。