鸿蒙应用开发-窥探: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 小时前
鸿蒙 Next 电商应用安全支付与密码保护实践
前端·harmonyos
SuperHeroWu72 小时前
【HarmonyOS】键盘遮挡输入框UI布局处理
华为·harmonyos·压缩·keyboard·键盘遮挡·抬起
sanzk6 小时前
华为鸿蒙应用开发
华为·harmonyos
SoraLuna11 小时前
「Mac畅玩鸿蒙与硬件28」UI互动应用篇5 - 滑动选择器实现
macos·ui·harmonyos
ClkLog-开源埋点用户分析12 小时前
ClkLog企业版(CDP)预售开启,更有鸿蒙SDK前来助力
华为·开源·开源软件·harmonyos
mg66812 小时前
鸿蒙系统的优势 开发 环境搭建 开发小示例
华为·harmonyos
lqj_本人12 小时前
鸿蒙next选择 Flutter 开发跨平台应用的原因
flutter·华为·harmonyos
lqj_本人12 小时前
使用 Flutter 绘制一个棋盘
harmonyos
lqj_本人15 小时前
Flutter&鸿蒙next 状态管理框架对比分析
flutter·华为·harmonyos
青瓷看世界17 小时前
华为HarmonyOS打造开放、合规的广告生态 - 插屏广告
华为·harmonyos·广告投放