鸿蒙应用开发-窥探:State装饰器(二)

说明

这部分资料是基于 3.0/4.0的文档。后续如有更新,请注意查看最新版本

状态变量到组件更新的原理

自定义组件的创建和渲染流程 章节里有如下的描述,非常有用

总结一下

只要状态变量被读取,就会保存两个map,一个是状态变量到组件,一个是组件到该组件的更新函数

那么状态变量变化时的流程就清晰了,首先通过状态变量找到组件,然后通过组件找到该组件的更新函数,执行一次

有状态变量的组件的更新函数

  1. 从上面的创建和渲染图里,我们看到了 observeComponentCreation
  2. 这个函数将自定义组件的创建包裹起来,其中自然也包括自定义组件的build函数。这样,当状态变量变化时,调用observeComponentCreation即可完成组件的重新创建和刷新
  3. 我们把arkui_ace_engine代码下载下来,搜索 observeComponentCreation,发现搜索结果还是挺多的。看到一个熟悉的pu_view.ts,咱们看一下

observeComponentCreation 观察组件创建

  1. 我们看到有两个观察组件创建的函数,一个是 observeComponentCreation,另一个是 observeComponentCreation2
  2. observeComponentCreation2 是2023年7月新加的,用于替换 observeComponentCreation。咱们继续搜索observeComponentCreation2

observeComponentCreation2 观察组件创建

搜索 observeComponentCreation2,我们发现函数定义的地方只有两个,一个是构建节点的类 builder_node.ts 一个是部分更新的类 pu_view.ts

builder_node.ts UI节点

在这里我们看到了之前提到的一个Map以及刷新函数。 同时我们也看到它是通过new Map创建的

pu_view.ts UI部分更新

这个类里面的处理和上面的几乎一样,不多赘述

搜索= new Map()

为什么搜索= new Map(),是因为一共两个Map。咱们找一下第一个Map在哪里。我们查看mgmtState.js这个类里的结果 只在本类中搜索= new Map(),我们发现了class LocalStorageclass SubscriberManagerclass PersistentStorageclass Environmentclass Viewclass ViewPUclass ObservedPropertyAbstractPUclass SynchedPropertyOneWayPUclass RecycleManager等多个类

经过筛选,我们发现了一个有趣的东西, this.trackedObjectPropertyDependencies_ = new Map();。它本身是一个内部类的一个方法,我们看一下这个内部类

搜索 this.trackedObjectPropertyDependencies_.set

看一下set方法,也就是存储kv的地方在哪,存了什么东西。

可以看到实际上key为状态变量,value则是组件的集合Set

搜索 addTrackedObjectPropertyDependency

搜索一下调用set的地方有哪些入口

搜索 this.recordTrackObjectPropertyDependencyForElmtId

继续搜索下面方法的调用,我们找到四处结果,都是 onOptimisedObjectPropertyRead(readObservedObject, readPropertyName, isTracked)内部调用的

  1. @ObjectLink状态变量的实现
  2. @State和@Provide 状态变量的实现
  3. 单向同步的状态变量实现,主要指@Prop系列
  4. 双向同步的状态变量实现,@Link和@Consume

搜索 .onOptimisedObjectPropertyRead

出现了四个和上面对应的单独ts文件。 先不用管,咱们继续看 stateMgmt.js这个类中 this.onOptimisedObjectPropertyRead 的调用,我们选取一个查看

class ObservedPropertyPU get

可以看到当状态变量的get方法被使用时,添加kv映射。当状态变量的set方法被使用时,通过kv之后找到组件进行更新渲染

ObservedObject.registerPropertyReadCb

ObservedObject这个类也在stateMgmt.js里面,所以直接在本类中搜索就行

SubscribableHandler.SET_ONREAD_CB

可以看到它是通过Symbol来实现的,保证独一无二

this.wrappedValue_ 哪来的

看一下咱们上面截图的set方法,它调用了一个setValueInternal,ok看一下这个方法 可以看到分三种情况,我们目前只关心1和3 一、本身已经是一个可观察对象,比如@Observed装饰的class

只是将组件放到了可观察对象的一个属性上,属性name也是一个Symbol

三、本身不是可观察对象。比如基础值

把基础值包装成了一个可观察对象

查看 class ObservedPropertyPU set 方法

查看 this.notifyPropertyHasChangedPU.方法

可以看到,先同步状态变量,之后再通知@Watch订阅的回调

查看 this.notifyTrackedObjectPropertyHasChanged 方法

可以看到,调用了viewPropertyHasChanged并将所有依赖这个状态变量的组件传进去了

查看 viewPropertyHasChanged

可以看到将需要更新的组件都添加到了 dirtDescendantElementIds_ 我们找下 dirtDescendantElementIds_ 的 forEach或者for...of调用

通过代码我们知道 父组件肯定在子组件前更新 我们再搜索 updateDirtyElements,发现 stateMgmt里面只有重用的时候调用了,但是其他文件里的rerender里都是调用的updateDirtyElements

总结一下

  1. 我们声明的每一个状态变量,会被包装成一个ObservedObject,这个类里面重写 get set方法

  2. get方法:我们访问这个状态变量时,将存储状态变量到组件集合这个kv存储下来

  3. set方法:状态变量改变时会做以下步骤

    a. 先更新关联的状态变量。比如@State变化,先更新关联的@Prop或@Link状态变量

    b. 通过map获取到依赖的组件,将组件标记为dirty。等待下次渲染更新UI

  4. 子组件的状态变量,通过第三步的a操作也发生变化,之后就和上面三步是一样的了。可以看出这是一个递归的操作过程

如果您仔细看了状态管理篇章 @Link装饰器:父子双向同步。您会发现和里面介绍的一模一样。咱们只是从代码层面验证了一遍

好了,感谢您的阅读。我写的比较浅显,如果您有精力还可以研究的更深,还有更多小细节。

资料

  1. 页面和自定义组件生命周期
  2. Arkui 引擎仓库
  3. @Link装饰器:父子双向同步
相关推荐
SameX1 小时前
HarmonyOS Next 安全生态构建与展望
前端·harmonyos
SameX1 小时前
HarmonyOS Next 打造智能家居安全系统实战
harmonyos
Random_index9 小时前
#Uniapp篇:支持纯血鸿蒙&发布&适配&UIUI
uni-app·harmonyos
鸿蒙自习室12 小时前
鸿蒙多线程开发——线程间数据通信对象02
ui·harmonyos·鸿蒙
SuperHeroWu715 小时前
【HarmonyOS】鸿蒙应用接入微博分享
华为·harmonyos·鸿蒙·微博·微博分享·微博sdk集成·sdk集成
zhangjr057517 小时前
【HarmonyOS Next】鸿蒙实用装饰器一览(一)
前端·harmonyos·arkts
诗歌难吟4641 天前
初识ArkUI
harmonyos
SameX1 天前
HarmonyOS Next 设备安全特性深度剖析学习
harmonyos
郭梧悠1 天前
HarmonyOS(57) UI性能优化
ui·性能优化·harmonyos
郝晨妤1 天前
鸿蒙原生应用开发元服务 元服务是什么?和App的关系?(保姆级步骤)
android·ios·华为od·华为·华为云·harmonyos·鸿蒙