10 @ObservedV2 状态管理在短视频应用中的实践
一、引言
HarmonyOS ArkUI V2 引入了@ObservedV2、@Trace、@Monitor等装饰器,构建了新一代响应式状态管理体系。本文分析多设备短视频项目中如何利用这些装饰器实现高效的跨组件状态管理。
二、装饰器体系概览
| 装饰器 | 作用范围 | 用途 | 示例 |
@ObservedV2 | 类 | 标记类为可观察对象 | @ObservedV2 class WindowInfo |
@Trace | 类属性 | 标记属性为可观察 | @Trace public widthBp: WidthBreakpoint |
@ComponentV2 | 组件 | 标记组件使用 V2 状态管理 | @ComponentV2 export struct AdaptiveVideo |
@Local | 组件属性 | 本地响应式状态 | @Local showComment: boolean = false |
@Param | 组件属性 | 父组件传入的参数 | @Param currentSource: string |
@Event | 组件属性 | 回调事件 | @Event onStateNotify: (state) => void |
@Monitor | 方法 | 监听属性变化 | @Monitor('currentIndex') onIndexChange() |
@Provider | 组件属性 | 提供共享状态 | @Provider('pathStack') pathStack |
@Consumer | 组件属性 | 消费共享状态 | @Consumer('showSideComment') showSideComment |
@Require | 组件属性 | 标记必填参数 | @Require @Param data: MSVDataModel[] |
@BuilderParam | 组件属性 | Builder 插槽 | @BuilderParam content: BuilderCallback |
三、状态管理架构
3.1 全局状态(AppStorageV2)
窗口信息通过AppStorageV2实现跨页面共享:
@ObservedV2 export class WindowInfo { @Trace public widthBp: WidthBreakpoint = WidthBreakpoint.WIDTH_XS; @Trace public windowSize: window.Size = { width: 0, height: 0 }; @Trace public statusBarTopVp: number = 0; @Trace public navigationBarBottomVp: number = 0; // ... } export function getWindowInfoState(): WindowInfo { return AppStorageV2.connect<WindowInfo>( WindowInfo, WINDOW_INFO_STORAGE_KEY, () => new WindowInfo() )!; }任何组件通过@Local windowInfo: WindowInfo = getWindowInfoState()获取全局窗口状态,且当WindowInfo中任意@Trace属性变化时自动刷新 UI。
3.2 组件树状态(@Provider/@Consumer)
根组件提供共享状态,子组件按需消费:
Index.ets (Provider) ├── @Provider pathStack: NavPathStack ├── @Provider showSideComment: boolean ├── @Provider showSideIndividual: boolean ├── @Provider isDark: boolean ├── @Provider subTabIndex: number ├── @Provider holdingHandStatus: HoldingHandStatus │ ├── AdaptiveVideo.ets (Consumer) │ ├── @Consumer showSideComment │ ├── @Consumer showSideIndividual │ ├── @Consumer pathStack │ ├── @Provider duration │ ├── @Provider currentTime │ └── @Provider windowSizeWidth / windowSizeHeight │ ├── AdaptiveAVPlayer.ets (Consumer) │ ├── @Consumer currentTime │ ├── @Consumer duration │ └── @Consumer windowSizeWidth / windowSizeHeight │ ├── IndividualByRouter.ets (Consumer) │ ├── @Consumer pathStack │ └── @Consumer showSideIndividual │ └── SplitComment.ets (Consumer) ├── @Consumer showSideComment ├── @Consumer pathStack └── @Provider commentCount3.3 组件内部状态(@Local)
每个组件维护自己的 UI 状态:
// AdaptiveVideo.ets @Local curIndex: number = 0; @Local angle: number = 0; @Local showComment: boolean = false; @Local currentState: media.AVPlayerState = 'idle'; @Local isSeeking: boolean = false; @Local seekToTime: number = -1; @Local progressBarWidth: number = 0;四、@Monitor 响应式监听
@Monitor装饰器是 V2 的核心能力,自动监听指定属性的变化并执行回调:
// 监听当前视频索引变化,控制播放/暂停 @Monitor('currentIndex') onIndexChange() { if (this.currentIndex !== this.index) { this.avPlayer?.pause(); } else { this.changePortraitVideo(); if (this.hasPrepared) this.avPlayer?.play(); else this.initAVPlayer(); } } // 监听窗口尺寸变化,重新计算视频沉浸式布局 @Monitor('windowSizeWidth', 'windowSizeHeight') onWindowSizeChange() { this.changePortraitVideo(); } // 监听进度条拖拽 @Monitor('seekToTime') onSeekToTimeChange() { if (this.seekToTime >= 0 && this.avPlayer && this.canSeek()) { this.avPlayer.seek(Math.floor(this.seekToTime), SeekMode.SEEK_CLOSEST); } }五、@Event 回调机制
@Event用于子组件向父组件通信:
// 子组件定义事件 @Event onStateNotify: (state: media.AVPlayerState) => void = () => {}; // 子组件触发 this.onStateNotify(state); // 父组件传入回调 AdaptiveAVPlayer({ onStateNotify: (state: media.AVPlayerState) => { this.currentState = state; } })六、@Param 参数传递
父组件通过@Param向子组件传递数据,支持@Require标记必填参数:
// 子组件 @Require @Param data: MSVDataModel[]; @Param index: number = 0; @Param useFloating: boolean = true; // 父组件使用 MSVTabs({ data: this.data, index: 0, useFloating: false })七、数据流总结
AppStorageV2 (全局) ──→ WindowInfo (窗口状态) │ ▼ @Provider (根组件) ──→ pathStack, showSideComment, isDark, ... │ ▼ @Consumer (子组件) ──→ 消费共享状态 │ ▼ @Local (组件内部) ──→ UI 状态 │ ▼ @Monitor ──→ 响应式监听,执行业务逻辑 │ ▼ @Event ──→ 子→父 回调通知八、最佳实践
- 全局状态最小化:只将真正需要跨组件共享的状态放入
AppStorageV2或@Provider - @Local 优先:组件内部状态优先使用
@Local,避免过度共享 - @Monitor 代替手动监听:利用
@Monitor自动监听属性变化,减少手动事件注册和清理 - @Event 代替回调参数:子组件向父组件通信使用
@Event,保持类型安全 - 避免循环依赖:
@Provider/@Consumer的 key 值命名要全局唯一,避免命名冲突