
23 — Monitor 与 Computed计算与监听一、引言在状态管理 V2 中Trace 解决了数据可观察的问题但数据变化后要做什么仍需要一种显式机制Monitor 监听 Trace 属性变化并触发业务逻辑Computed 提供带缓存的派生计算属性。本项目播放器组件features/multishortvideoadaptivevideo/src/main/ets/view/AdaptiveAVPlayer.ets用三个 Monitor 串起了滑动切换视频、窗口尺寸变化、拖动进度条三条关键联动链路是理解 V2 监听机制的绝佳样本。本文结合该文件逐一拆解 Monitor 的用法与 Computed 的适用场景。二、Monitor 基本用法Monitor 装饰方法监听一个或多个 Trace或 Local/Param 等属性属性变化时自动调用。AdaptiveAVPlayer.ets 中第一个 Monitor 处理当前播放下标变化// features/multishortvideoadaptivevideo/src/main/ets/view/AdaptiveAVPlayer.ets真实代码Monitor(currentIndex)onIndexChange(){if(this.currentIndex!this.index){// 非当前项暂停播放节省资源this.avPlayer?.pause().catch((){Logger.error(TAG,Pause AVPlayer failed.);})}else{this.changePortraitVideo();if(this.hasPrepared){if(this.canPlay()){this.avPlayer?.seek(0,media.SeekMode.SEEK_CLOSEST);this.avPlayer?.play().catch((){Logger.error(TAG,Play AVPlayer failed);})}}else{this.initAVPlayer()}}}当前后滑动手势切换视频时父组件 AdaptiveVideo 在 Swiper 的 onAnimationStart 中更新this.curIndex并传给播放器的 Param currentIndexMonitor 随即被触发非当前页暂停、当前页回到起点并播放或初始化。业务逻辑与 UI 刷新解耦且天然携带哪个属性变了的上下文。三、Monitor 多属性监听与完整方法签名Monitor 支持一次监听多个属性AdaptiveAVPlayer.ets 中窗口尺寸联动即如此// 真实代码窗口宽高任一变化都重算视频显示区域Monitor(windowSizeWidth,windowSizeHeight)onWindowSizeChange(){this.changePortraitVideo();}Monitor 方法还可接收 IMonitorChange 参数获取变化前后的值Monitor(seekToTime)onSeekToTimeChange(change:IMonitorChange){constoldValuechange.value?.[seekToTime]asnumber;// 变化前constnewValuechange.changeValue?.[seekToTime]asnumber;// 变化后if(this.seekToTime0this.avPlayerthis.canSeek()){this.avPlayer.seek(Math.floor(this.seekToTime),media.SeekMode.SEEK_CLOSEST);}}change.value记录属性变化前的值change.changeValue记录变化后的值可用于差值计算、埋点上报或恢复现场如拖拽进度时记住原进度取消拖拽可回退。IMonitorChange 的结构可归纳为字段含义value变化前各监听属性的值集合changeValue变化后各监听属性的值集合keys本次实际变化的属性名数组keys在多属性监听时尤其有用即使同时监听宽高也能通过 keys 判断这次是宽度变了还是高度变了从而决定是否触发重排。项目中的Monitor(seekToTime)正是拖动进度条的核心链路用户在进度条上拖动 → 父组件 AdaptiveVideo 通过 updateSeekByPosition 计算目标时间并更新 seekToTime → Monitor 触发 seek 跳转配合 isSeeking 标志位区分拖拽预览与播放推进从而实现了拖到哪播到哪的实时预览。四、Computed带缓存的派生属性Computed 装饰 getter 方法基于 Trace 属性派生出新值且只在依赖项变化时重新计算结果自动缓存。典型场景是把播放进度换算为进度条百分比或时间文本// 扩展示例基于项目 currentTime/duration 的派生计算项目风格ComponentV2struct AdaptiveVideo{Consumer(currentTime)currentTime:number0;Consumer(duration)duration:number0;ComputedgetprogressPercent():number{if(this.duration0){return0;}returnMath.min(100,(this.currentTime/this.duration)*100);}ComputedgettimeText():string{consttotalMath.floor(this.duration/1000);constcurMath.floor(this.currentTime/1000);constformat(sec:number):string${Math.floor(sec/60)}:${String(sec%60).padStart(2,0)};return${format(cur)}/${format(total)};}}与每次渲染都执行计算的普通 getter 不同Computed 只在依赖的 Trace 属性变化时重算timeUpdate 每 500ms 触发一次 currentTime 更新时只有真正变化才触发重算避免高频渲染下的无谓计算。派生逻辑集中收口在组件内也提升了可读性。需要说明的是Computed 只读不可写若需要百分比 → 目标时间的反向换算拖动进度条时把手指位置换算为 seekToTime仍应写在普通方法中项目 AdaptiveVideo.ets 的 updateSeekByPosition 即为此类单向数据流下计算与赋值各司其职。五、Monitor 与 Watch 对比维度V1 WatchV2 Monitor获取变化前后值不支持支持IMonitorChange多属性监听单属性支持可监听多个依赖的观察体系State 等 V1Trace 等 V2 属性触发时机属性变化后属性变化后异步批量应用场景简单回调联动播放、窗口重算、持久化Monitor 还具备去重与批量能力同一帧内多次修改被观察属性方法只会执行一次天然适合合并窗口 resize 这类高频事件。AdaptiveAVPlayer.ets 中窗口尺寸监听只做一次重算正是受益于此。此外Monitor 对 Local、Param、Consumer 等 V2 装饰器属性同样生效AdaptiveAVPlayer.ets 中 currentIndex 是 Param父组件传入、seekToTime 是 Param、windowSizeWidth/windowSizeHeight 是 Consumer三种来源的属性变化都能触发对应监听说明 Monitor 与状态从哪来无关只关心属性值是否变化。六、使用注意事项Monitor 不能修改被监听的属性本身否则会形成递归触发应通过其他状态或事件回传。方法体内优先读取变化后的最新值如需历史值用 IMonitorChange 的 change.value。Computed 依赖的必须是 Trace/Local 等可观察属性普通成员变量变化不会触发重算。播放器回调onStateChange、onTimeUpdateFunction中直接赋值 Consumer 属性配合 Monitor 联动 UI注意不要在 Monitor 中做耗时操作。组件销毁时 Monitor 自动解绑无需手动注销但播放器等原生资源仍需在 aboutToDisappear 中显式释放。七、总结与最佳实践用 Monitor 把状态变化与业务副作用显式关联切视频暂停/起播、窗口变化重排、进度跳转都收敛为一条清晰的联动链路。多属性联动的场景宽高、坐标一次 Monitor 监听多个属性利用批量去重天然抗抖。需要变化前后值拖拽回退、差值统计时使用 IMonitorChange 参数。派生展示值百分比、时间文本、格式化文案用 Computed享受缓存的同时保持代码整洁。坚持单向数据流Monitor 只做副作用UI 刷新交给 Trace 自身完成二者职责不重叠。