当前位置: 首页 > news >正文

HarmonyOs~ArkUI进阶 之 状态管理

合理选择装饰器

最小化状态共享范围

在没有强烈的业务需求下,尽可能按照状态需要共享的最小范围选择合适的装饰器。

应用开发过程中,按照组件颗粒度,状态一般分为组件内独享的状态和组件间需要共享的状态

组件间需要共享的状态

组件间需要共享的状态,按照共享范围从小到大依次有三种场景:

父子组件间共享状态,不同子树上组件间共享状态和不同组件树间共享状态

  • 父子组件间共享状态:如下图,
  • ”父组件”和其子组件”子组件A”、”子组件B”共享状态loading。
  • 图3 父子组件间共享状态场景

  • 不同子树上组件间共享状态:
  • 如下图,祖先组件的左子树上”孙子组件AAA”和右子树上”孙子组件BAA”共享状态loading。
  • 图4 不同子树上组件间状态共享场景

  • 不同组件树间共享状态:
  • 如下图,组件树A内”子组件AA”和组件树B内”孙子组件AAA”共享状态loading。
  • 图5 不同组件树间共享状态的场景

对于上述三种场景,ArkUI提供了@State+@Prop、@State+@Link、@State+@Observed+@ObjectLink、@Provide+@Consume、AppStorage、LocalStorage六种装饰器组合以解决不同范围内的组件间状态共享。按照共享范围能力从小到大,各装饰器组合的共享范围能力和生命周期如下:

  1. @State+@Prop、@State+@Link、@State+@Observed+@ObjectLink:三者的共享范围为从@State所在的组件开始,到@Prop/@Link/ObjectLink所在组件的整条路径,路径上所有的中间组件通过@Prop/@Link/@ObjectLink都可以共享同一个状态。@State修饰的状态和其所属的自定义组件共享生命周期,在组件内定义时创建,组件销毁时被回收。@Link装饰的变量和其所属的自定义组件共享生命周期。@ObjectLink装饰的变量和其所属的自定义组件共享生命周期。(父子
  2. @Provide+@Consume:状态共享范围是以@Provide所在组件为祖先节点的整棵子树,子树上的任意后代组件通过@Consume都可以共享同一个状态。@Provide修饰的变量与其所属的组件绑定,在组件内定义时被创建,在组件销毁时被回收。(根节点为祖先节点以下的子节点都可获取到)
  3. LocalStorage:共享范围为UIAbility内以页面为单位的不同组件树间的共享。存储在LocalStorage中的状态的生命周期与LocalStorage绑定。LocalStorage的声明周期由应用程序决定,当应用释放最后一个指向LocalStorage的引用时,LocalStorage被垃圾回收。(同页面的不同组件树间的共享)
  4. AppStorage:共享范围是应用全局。AppStorage与应用的进程绑定,由UI框架在应用程序启动时创建,当应用进程终止,AppStorage被回收。存储在AppStorage中的状态的生命周期与LocalStorage绑定。(全局变量)

按照软件开发原则,应优先选择共享范围能力小的装饰器方案,减少不同模块间的数据耦合,便于状态及时回收。

建议选择装饰器的优先级为:@State+@Prop、@State+@Link、@State+@Observed+@ObjectLink > @Provide+@Consume > LocalStorage > AppStorage。

减少不必要的参数层层传递

看个示例图

项目中使用Navigation组件管理路由,定义“appNavigationStack”变量表示应用当前的路由信息。现“DiscoverView”组件和“ResourceListView”组件需要共享路由信息状态。按照@State+@Prop层层传递的方案,当前各组件的设计如下:

图7 @State+@Prop当前组件设计图

层层传递,直到两个需要共享状态的组件

若此时产品需要新增功能,该功能要求在“DiscoverView”组件的后代“ActionButtonView”组件上新增对路由信息的判断逻辑。此时开发者需修改上述各个组件设计如下图所示:

图8 @State+@Prop新增功能后组件设计图

可以看到,新功能的逻辑原本只是在“ActionButtonView”这一个组件中使用,开发者却需要修改从“DiscoverView”组件到“ActionButtonView”组件路径上3个组件的结构。若当业务后续再次变更为无需使用该状态时,也同样需要修改多个组件。这显然不是很好的实现方案。

此时使用@Provide+@Consume方案更为合理。同样是“ResourceListView”组件和“DiscoverView”组件共享状态,此方案各组件设计如下:

图9 使用@Provide+@Consume方式当前各组件设计

通过在最顶部组件“MainPage”中注入key值为“appNavigationStack”的路由信息状态,其后代组件均可以通过@Consume装饰器获取该状态值。当业务变动需要“DiscoverView”的后代“ActionButtonView”组件也共享路由信息时,此方案只需在组件“ActionButtonView”上使用@Consume装饰器直接获取路由信息状态,而无需修改其他组件。此时各组件设计如下:

图10 使用@Provide+@Consume方式业务变动后各组件设计

因此当共享状态的组件间跨层级较深时,或共享的信息对于整个组件树是“全局”的存在时,选择@Provide+@Consume的装饰器组合代替层层传递的方式,能够提升代码的可维护性和可拓展性。

按照状态复杂度选择装饰器

  1. @State+@Prop组合方案:
    • @Prop装饰器支持接收Object、class、string、number、boolean、enum类型,以及这些类型的数组。
    • @Prop装饰的变量是对父组件传入状态值的深拷贝,当@Prop装饰器装饰的变量为复杂Object、class或其类型数组时,会增加状态创建时间以及占用大量内存。
    • @Prop装饰的变量和父组件是单向绑定的关系。当父组件数据源发生变化时,接收该数据源的@Prop所在组件的实例会重新渲染。 当该组件内被@Prop装饰的变量被修改时,父组件数据源不会变化,父组件实例也不会重新渲染。
  2. @State+@Link组合方案:
    • @Link装饰器支持接收Object、class、string、number、boolean、enum类型,以及这些类型的数组。
    • @Link装饰器修饰的变量是对父组件传入状态的引用的拷贝,两者指向同一个地址。当状态是简单数据类型或简单Object类型时,@Link和@Prop在状态创建时间和内存的占用方面区别不大。当状态为复杂的Object、class或其类型数组时,@Link相较@Prop能明显减少状态创建时间和内存的占用。
    • @Link装饰器的变量和父组件是双向绑定的关系。当父组件数据源发生变化时,接收该数据源的@Link所在组件的实例会重新渲染。 当该组件内被@Link装饰的变量被修改时,父组件数据源会同步修改,父组件实例也会重新渲染。
  3. @State+@Observed+@ObjectLink组合方案:
    • @ObjectLink只支持接收被@Observed装饰的class实例及继承Date或者Array的class实例。
    • @ObjectLink装饰的变量是只读的,不支持对状态重新赋值。
    • @ObjectLink必须配合@Observed使用,它的设计是为了解决对嵌套类对象属性变化的监听,如需要观察对象数组中单个数据项的属性值变化,或嵌套对象的对象类型属性的子属性变化。

结合三个方案的特性,在选择时有如下建议:

  • 需要观察嵌套类对象的深层属性变化的场景,选择@State+@Observed+@ObjectLink。
  • 状态是复杂对象、类或其类型数组的场景,选择@State+@Link。
  • 状态是简单数据类型时,使用@State+@Link和@State+@Prop均可。在功能层面上,依据@Prop单向绑定的特性,@State+@Prop适合用于非实时修改的场景,如编辑电话薄联系人信息时,展示编辑界面的子组件信息的修改要求不实时同步回父组件,需要等到编辑完成后点击“确认”按钮时才会以事件驱动的方式修改父组件的状态。依据@Link双向绑定的特性,@State+@Link适合用于实时修改的场景,如组件嵌套时的滚动条同步。

总结

在实际开发中,合理选择装饰器主要包含以下三步:

1.首先根据状态需要共享的范围大小,尽量选择共享能力小的装饰器方案,优先级依次为@State+@Prop、@State+@Link或@State+@Observed+@ObjectLink > @Provide+@Consume > LocalStorage > AppStorage。

2.当共享的状态的组件间层级相差较大时,为避免较差的代码可扩展性和可维护性,@Provide+@Consume的方案要优于层层传递的共享方案。

3.对于具有相同优先级的@State+@Prop、@State+@Link或@State+@Observed+@ObjectLink 三个方案,应结合状态的复杂程度和装饰器各自的特性选择。

实际开发中,应根据业务需求衡量优先级选择合适的装饰器,整体可参考如下建议:

  1. @State+@Prop:适合状态结构简单,且共享状态的组件间层级相差不大的场景。或功能上要求子组件不实时同步修改给父组件的场景。
  2. @State+@Link:适合状态结构复杂,且共享状态的组件间层级相差不大的场景。或功能上要求子组件对状态的修改实时同步给父组件的场景。
  3. @State+@Observed+@ObjectLink:适合需要观察嵌套类对象的子属性变化的场景或对象数组的数据项属性变化的场景,如监听列表卡片上某个属性的变化。
  4. @Provide+@Consume:适合用于对于整个组件树而言“全局”的状态,且该状态改动不频繁的状态共享场景,如共享界面的路由信息。
  5. AppStorage:适合对于整个应用而言“全局”的变量或应用的主线程内多个UIAbility实例间的状态共享,如用户信息。
  6. LocalStorage:适合对于单个Ability而言“全局”的变量,主要用于不同页面间的状态共享场景。

 精细化拆分复杂状态

第一种方式

使用AppStorage存储用户信息UserData,将收藏的id加入到UserData这个类中 那么探索页面的收藏发生改变 我的页面自然同步 但是有一点不好  我的页面一旦我的信息发生修改 那么AppStorage存储用户信息UserData一旦监听到修改就会同步更新探索和我的页面  其实探索页根本不需要存储用户信息只需要存储收藏的id数组就行 这样我们就分开存储 

单独存储下id就好了 在此方案中,由于文章卡片组件没有绑定AppStorage中key值为“userData”的变量,当用户编辑修改了用户描述userData.description的值时, 文章卡片组件不会重新渲染。

 因此,从性能的角度考虑,在使用LocalStorage或AppStorage装饰器存储状态变量时需要合理设计状态的数据结构,避免无意义的渲染刷新。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • Three.js投射光线实现三维物体交互
  • 抖音本地生活城市服务商骗局后,第三方本地生活系统源码部署持续火爆!
  • Pyqt5新手教程
  • 【b站-湖科大教书匠】6 应用层 - 计算机网络微课堂
  • NSSRound#4 Team
  • C++初阶学习第三弹——类与对象(上)
  • vue的nextTick的作用
  • leetcode-136. 只出现一次的数字
  • C#中的异步编程:如何有效地使用async和await关键字以提高应用程序的性能和响应性
  • 【linux】在多核CPU下,好像看到不同进程在不同CPU调度
  • vue js 将对象转换为 JSON 字符串 ;将 JSON 字符串转换为对象
  • 人工智能与机器学习原理精解【9】
  • SQL进阶技巧:车辆班次问题分析
  • Typescript配置文件(tsconfig.json)详解系列四:esModuleInterop和allowSyntheticDefaultImports
  • Redis7-入门-安装
  • .pyc 想到的一些问题
  • 【5+】跨webview多页面 触发事件(二)
  • 【个人向】《HTTP图解》阅后小结
  • angular组件开发
  • interface和setter,getter
  • Promise面试题2实现异步串行执行
  • Spring技术内幕笔记(2):Spring MVC 与 Web
  • webpack入门学习手记(二)
  • 从零到一:用Phaser.js写意地开发小游戏(Chapter 3 - 加载游戏资源)
  • 给新手的新浪微博 SDK 集成教程【一】
  • 面试题:给你个id,去拿到name,多叉树遍历
  • 如何合理的规划jvm性能调优
  • 学习笔记TF060:图像语音结合,看图说话
  • 优秀架构师必须掌握的架构思维
  • 怎么将电脑中的声音录制成WAV格式
  • 正则表达式
  • 最近的计划
  • gunicorn工作原理
  • # 详解 JS 中的事件循环、宏/微任务、Primise对象、定时器函数,以及其在工作中的应用和注意事项
  • #免费 苹果M系芯片Macbook电脑MacOS使用Bash脚本写入(读写)NTFS硬盘教程
  • (06)Hive——正则表达式
  • (70min)字节暑假实习二面(已挂)
  • (C语言)二分查找 超详细
  • (NSDate) 时间 (time )比较
  • (poj1.2.1)1970(筛选法模拟)
  • (转)大道至简,职场上做人做事做管理
  • (转载)PyTorch代码规范最佳实践和样式指南
  • .form文件_SSM框架文件上传篇
  • .net core 6 redis操作类
  • .NET IoC 容器(三)Autofac
  • .NET 使用 JustAssembly 比较两个不同版本程序集的 API 变化
  • .NET6 开发一个检查某些状态持续多长时间的类
  • .NET开源快速、强大、免费的电子表格组件
  • .NET命名规范和开发约定
  • [20150629]简单的加密连接.txt
  • [APUE]进程关系(下)
  • [C#][opencvsharp]opencvsharp sift和surf特征点匹配
  • [C#]winform制作圆形进度条好用的圆环圆形进度条控件和使用方法
  • [C/C++]关于C++11中的std::move和std::forward
  • [CareerCup][Google Interview] 实现一个具有get_min的Queue