鸿蒙应用开发-窥探: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装饰器:父子双向同步
相关推荐
2501_920627611 小时前
Flutter 框架跨平台鸿蒙开发 - 派对策划助手应用
flutter·华为·harmonyos
沙雕不是雕又菜又爱玩2 小时前
基于HarmonyOS的笔记管理应用
harmonyos
@不误正业2 小时前
AI Agent多轮对话管理:3大架构源码级实现与性能对比(附鸿蒙实战)
人工智能·架构·harmonyos
里欧跑得慢2 小时前
Flutter 组件 powersync_core 的适配 鸿蒙Harmony 实战 - 驾驭极致离线优先架构、实现鸿蒙端高性能 SQL 增量同步与数据安全治理方案
flutter·harmonyos·鸿蒙·openharmony·powersync_core
轻口味3 小时前
HarmonyOS 6 自定义人脸识别模型9:基于tflite的人脸识别模型转换
华为·harmonyos
芙莉莲教你写代码3 小时前
Flutter 框架跨平台鸿蒙开发 - 网络安全学习应用
学习·web安全·flutter·华为·harmonyos
互联网散修4 小时前
零基础鸿蒙应用开发第二十五节:接口的行为契约能力
华为·harmonyos
@不误正业4 小时前
AI Agent工具调用深度实战-从Function-Calling到鸿蒙设备控制全链路
人工智能·华为·harmonyos
云和数据.ChenGuang6 小时前
鸿蒙应用对接扣子智能体:从 0 到 1 落地 AI 智能体集成
人工智能·华为·harmonyos
HwJack207 小时前
HarmonyOS开发中ArkTS @Styles装饰器:告别复制粘贴,拥抱优雅的样式复用
华为·harmonyos