第七色在线视频,2021少妇久久久久久久久久,亚洲欧洲精品成人久久av18,亚洲国产精品特色大片观看完整版,孙宇晨将参加特朗普的晚宴

為了賬號(hào)安全,請(qǐng)及時(shí)綁定郵箱和手機(jī)立即綁定

HarmonyOS5 源碼分析 —— 生命周期與狀態(tài)管理(2)

標(biāo)簽:
鴻蒙 HarmonyOS OpenHarmony

一、前言

在前文中,我们提到过 “状态管理”
但状态管理并不仅仅是 “数据变化 → UI 更新” 这么简单,它还与组件的 创建、复用、销毁 等生命周期过程密切相关。

理解状态管理与生命周期之间的关系,可以帮助我们规避 内存泄漏状态残留 等常见问题。

因此,本文将带你一起梳理 生命周期与状态管理的交互机制,并通过示例分析如何在实践中避免踩坑。

如果您有任何疑问、对文章写的不满意、发现错误或者有更好的方法,如果你想支持下一期请务必点赞~,欢迎在评论、私信或邮件中提出,非常感谢您的支持。🙏

二、阶段一:组件创建与状态初始化

组件刚出生时,状态管理的首要任务就是——初始化状态变量,并为后续的依赖收集准备好“档案”。

  • 构造函数中的状态初始化

    在构造函数执行阶段,状态管理相关的数据结构会被搭好,但此时 依赖收集还没开工。可以理解为,你先把仓库搭起来,后面才能往里面存货。

    (看完图你就可以跳到八了,嘻嘻)

    Untitled diagram _ Mermaid Chart-2025-08-10-073923
  • 装饰器元信息的注册

    装饰器在构造函数执行时就会注册元信息,为后续的依赖收集做准备。

    // ViewV2 构造函数
    constructor(parent: IView, elmtId: number = UINodeRegisterProxy.notRecordingDependencies, extraInfo: ExtraInfo = undefined) {
        super(parent, elmtId, extraInfo);
        // 状态变量在这里初始化
        this.dirtDescendantElementIds_ = new Set<number>();
        this.monitorIdsDelayedUpdate = new Set();
        this.computedIdsDelayedUpdate = new Set();
        // ... 其他状态初始化
    }
    

三、阶段2:依赖收集与UI渲染

当组件进入渲染阶段时,状态管理系统会开始记录 哪些 UI 元素依赖了哪些状态变量

  • 开始依赖收集

    收集必须发生在 startRecordDependenciesstopRecordDependencies 之间,这样依赖关系才不会乱。

    // @ObservedV2 装饰器
    function ObservedV2<T extends ConstructorV2>(BaseClass: T): T {
        ConfigureStateMgmt.instance.usingV2ObservedTrack(`@ObservedV2`, BaseClass?.name);
        return observedV2Internal<T>(BaseClass);
    }
    
    // observedV2Internal 内部实现
    const observedClass = class extends BaseClass {
        constructor(...args) {
            super(...args);
            // 在构造函数中注册装饰器元信息
            // 这些元信息后续用于依赖收集和变更通知
        }
    }
    
  • 状态变量的读取与依赖建立

    每次读取状态变量时,系统会记下:“当前渲染的 elmtId 依赖了这个属性”。这就像贴便利贴,方便后续精准更新。

    // ViewV2.observeComponentCreation2
    public observeComponentCreation2(compilerAssignedUpdateFunc: UpdateFunc, classObject: { prototype: Object, pop?: () => void }): void {
        // ... 前置处理 ...
        
        const updateFunc = (elmtId: number, isFirstRender: boolean): void => {
            // 关键:开始依赖收集
            ObserveV2.getObserve().startRecordDependencies(this, elmtId);
            
            try {
                // 执行 build 函数,渲染UI
                compilerAssignedUpdateFunc(elmtId, isFirstRender);
            } finally {
                // 关键:停止依赖收集
                ObserveV2.getObserve().stopRecordDependencies();
            }
        };
    }
    

四、阶段3:状态变更与UI更新

这里有个误区:状态变更不是立刻就让 UI 刷新
HarmonyOS 走的是“先拦截、再批处理”的策略,减少性能损耗。

  • 状态变更的拦截

    系统用 Proxygetter/setter 来捕捉状态变化,然后发出“更新通知”。

    // @Trace 装饰器的 getter
    const Trace = (target: Object, propertyKey: string): void => {
        ObserveV2.addVariableDecoMeta(target, propertyKey, '@Trace');
        return trackInternal(target, propertyKey);
    };
    
    // trackInternal 内部实现(伪代码)
    function trackInternal(target: Object, propertyKey: string) {
        Reflect.defineProperty(target, propertyKey, {
            get() {
                // 关键:在 getter 中收集依赖
                ObserveV2.getObserve().addRef(this, propertyKey);
                return this[ObserveV2.OB_PREFIX + propertyKey];
            },
            set(val) {
                // 关键:在 setter 中通知变更
                if (val !== this[ObserveV2.OB_PREFIX + propertyKey]) {
                    this[ObserveV2.OB_PREFIX + propertyKey] = val;
                    ObserveV2.getObserve().fireChange(this, propertyKey);
                }
            }
        });
    }
    
  • 变更通知与UI标记

    不是马上更新,而是先把需要刷新的节点ID丢进“脏集合”,等待统一处理。

    // ObjectProxyHandler 的 set 方法
    set(target: any, key: string | symbol, value: any): boolean {
        const oldValue = target[key];
        if (oldValue === value) {
            return true; // 值没变,不触发更新
        }
        
        // 设置新值
        target[key] = value;
        
        // 关键:通知变更
        ObserveV2.getObserve().fireChange(conditionalTarget, key);
        return true;
    }
    
  • 批量UI更新

    等批量处理时,一次性刷新相关 UI,避免“频繁改一行代码、刷新整个页面”的低效操作。

    // ViewV2.uiNodeNeedUpdateV2
    public uiNodeNeedUpdateV2(elmtId: number): void {
        // 关键:将需要更新的 elmtId 加入脏集合
        this.dirtDescendantElementIds_.add(elmtId);
        
        // 创建属性变更函数
        const propertyChangedFunc: PrebuildFunc = () => {
            // 这里可以执行一些前置逻辑
        };
        
        // 标记需要更新
        this.markDirtyElement(elmtId, propertyChangedFunc);
    }
    

五、阶段4:组件复用与状态重置

组件不是用完就丢,HarmonyOS 会用 RecyclePoolV2 来“回收再利用”。

  • 组件复用的触发

    复用时会调用 aboutToReuseInternal 把状态重置干净,像清空缓存一样。

    // ViewV2.updateDirtyElements
    public updateDirtyElements(): void {
        // 获取所有需要更新的 elmtId
        const dirtElmtIdsFromRootNode = Array.from(this.dirtDescendantElementIds_);
        
        // 批量更新
        dirtElmtIdsFromRootNode.forEach(elmtId => {
            this.UpdateElement(elmtId);
            // 更新完成后从脏集合中移除
            this.dirtDescendantElementIds_.delete(elmtId);
        });
    }
    
  • 状态变量的重置

    保证下次用这个组件时,它的状态是全新的,不会“带病上岗”。

    // ViewV2.reuseOrCreateNewComponent
    public reuseOrCreateNewComponent(params: {
        componentClass: any, 
        getParams: () => Object,
        getReuseId?: () => string, 
        extraInfo?: ExtraInfo
    }): void {
        const reuseId = params.getReuseId?.() || 'default';
        const recyclePool = this.getOrCreateRecyclePool();
        
        // 尝试从回收池中获取可复用的组件
        let reuseComp = recyclePool.popRecycleV2Component(reuseId);
        
        if (reuseComp) {
            // 复用现有组件
            reuseComp.aboutToReuseInternal(params.getParams());
            // ... 其他复用逻辑
        } else {
            // 创建新组件
            // ...
        }
    }
    
  • 依赖关系的重建

    不光是数据要重置,监听器、计算属性、消费者这些依赖也要全部更新,避免数据错乱。

    // ViewV2.resetStateVarsOnReuse
    public resetStateVarsOnReuse(params: Object): void {
        // 重置所有状态变量
        Object.keys(params).forEach(key => {
            this.resetParam(key, params[key]);
        });
    }
    
    // VariableUtilV2.resetParam
    public static resetParam<Z>(target: object, attrName: string, newValue: Z): void {
        const meta = target[ObserveV2.V2_DECO_META]?.[attrName];
        VariableUtilV2.checkInvalidUsage(meta, attrName);
        
        const storeProp = ObserveV2.OB_PREFIX + attrName;
        if (newValue === target[storeProp]) {
            return; // 值没变,不触发更新
        }
        
        // 设置新值并通知变更
        target[storeProp] = newValue;
        ObserveV2.getObserve().fireChange(target, attrName);
    }
    

六、 阶段5:组件销毁与清理

最后一步,就是断干净一切联系,防止内存泄漏。

  • 依赖关系的清理

    销毁时要清空状态变量、监听器、计算属性和消费者,让组件彻底“归零”。

    // ViewV2.aboutToReuseInternal
    private aboutToReuseInternal(initialParams?: Object): void {
        // 重置状态变量
        if (initialParams) {
            this.resetStateVarsOnReuse(initialParams);
        }
        
        // 重置监听器和计算属性
        this.resetMonitorsOnReuse();
        Object.keys(this.computedIdsDelayedUpdate).forEach(name => {
            this.resetComputed(name);
        });
        
        // 重置消费者
        this.resetConsumer('', undefined);
    }
    

七、值得我们小心的

  • 避免在 build 中进行复杂计算

    相信大家可能会写出这样的代码:

    build() {
      Colum(){
      	Text(this.heavyComputation().toString())
      }
    }
    

    显示一切正常

    但是build 方法可能被频繁调用,复杂计算会严重影响性能。而@Computed 提供了智能缓存机制。所以更推荐你这么写:

    @Computed
    get expensiveResult() {
      return this.heavyComputation();  // 只在依赖变化时执行
    }
    
    build() {
      Colum(){
      	Text(this.expensiveResult.toString())
      }
    }
    
  • 合理使用 @Monitor 避免过度监听

    Monitor每一个用过的人都说爽,爽过头你就会写出这样的代码:

    @Monitor('user', 'profile', 'settings', 'preferences', 'theme')
    onUserChange() {
      // 任何相关变化都会触发
    }
    

    过度监听会导致不必要的回调执行,影响性能。应该只监听真正关心的数据路径。

    //只监听必要的路径
    @Monitor('user.profile')
    onProfileChange() {
      // 只在 profile 变化时触发
    }
    
  • 组件复用时的状态重置

    在repeat组件中,有时候会出现一些奇怪的显示结果,或者显示结果和事件结果不对应,可能就是因为组件被复用了:

    //依赖持久状态
    @Trace lastUpdateTime = Date.now();
    
    aboutToReuse() {
      // 这个时间戳在组件复用时不会重置!
      console.log('上次更新:', this.lastUpdateTime);
    }
    

    组件复用时,装饰器管理的状态会自动重置,但普通属性不会。这可能导致状态不一致。

    //在 aboutToReuse 中重置状态
    aboutToReuse() {
      this.lastUpdateTime = Date.now();  // 手动重置
    }
    

    如上!Very GGGood!

  • 异步操作中的依赖收集

    跨线程执行(比如 ArkUI TaskPool)时,代码运行在 Worker 线程,是没有直接访问 UI 响应式对象的能力的。

    即使访问到了,也只是访问了数据的“副本”,而不是原始的响应式代理对象。

    这样 this.count++ 只会改副本,不会触发主线程的 UI 更新。

    // ❌ 错误:在异步回调中直接修改状态
    setTimeout(() => {
      this.count++;  // 可能没有正确的依赖上下文
    }, 1000);
    

    异步操作可能发生在依赖收集上下文之外,直接修改状态可能导致依赖关系丢失。

    // ✅ 正确:使用状态管理的方法
    setTimeout(() => {
      this.updateCount(this.count + 1);  // 通过方法更新
    }, 1000);
    
    private updateCount(newValue: number) {
      this.count = newValue;  // 在正确的方法上下文中
    }
    
  • 状态变量的命名规范、

    开发中,我要使用清晰的,意义明确的命名

    // 清晰的命名
    @Trace userName = '';
    @Trace userAge = 0;
    @Trace isUserLoggedIn = false;
    
    //模糊的命名
    @Trace data = {};  // 不清楚具体是什么数据
    @Trace flag = true;  // 不清楚标志的含义
    

    你也不想搜个flag,一看200个的情况出现吧。

八、小结

大概就这些啦,总结下:

从组件的 创建与初始化依赖收集、状态变更,再到 复用与重置,直至 销毁与清理,生命周期的每一个阶段都与状态管理息息相关。
只有真正理解它们之间的交互机制,我们才能写出 高性能、无隐患、易维护 的应用。

掌握了这些底层逻辑,你会发现——很多“莫名其妙”的 UI Bug,其实都有迹可循;而那些难以捉摸的内存泄漏,也能被提前规避。

(那你知道为什么V2的属性全是__ob_开头了吗?)

如果您有任何疑问、对文章写的不满意、发现错误或者有更好的方法,如果你想支持下一期请务必点赞~,欢迎在评论、私信或邮件中提出,非常感谢您的支持。🙏

八、大结

没了。

如果您有任何疑问、对文章写的不满意、发现错误或者有更好的方法,如果你想支持下一期请务必点赞~,欢迎在评论、私信或邮件中提出,非常感谢您的支持。🙏

點(diǎn)擊查看更多內(nèi)容
TA 點(diǎn)贊

若覺得本文不錯(cuò),就分享一下吧!

評(píng)論

作者其他優(yōu)質(zhì)文章

正在加載中
  • 推薦
  • 評(píng)論
  • 收藏
  • 共同學(xué)習(xí),寫下你的評(píng)論
感謝您的支持,我會(huì)繼續(xù)努力的~
掃碼打賞,你說多少就多少
贊賞金額會(huì)直接到老師賬戶
支付方式
打開微信掃一掃,即可進(jìn)行掃碼打賞哦
今天注冊有機(jī)會(huì)得

100積分直接送

付費(fèi)專欄免費(fèi)學(xué)

大額優(yōu)惠券免費(fèi)領(lǐng)

立即參與 放棄機(jī)會(huì)
微信客服

購課補(bǔ)貼
聯(lián)系客服咨詢優(yōu)惠詳情

幫助反饋 APP下載

慕課網(wǎng)APP
您的移動(dòng)學(xué)習(xí)伙伴

公眾號(hào)

掃描二維碼
關(guān)注慕課網(wǎng)微信公眾號(hào)

舉報(bào)

0/150
提交
取消