
17 — Navigation 路由与 NavPathStack一、引言短视频应用中视频页 → 评论区 / 个人主页的跳转高频且要求无闪烁。HarmonyOS 的 Navigation 配合 NavPathStack 提供声明式路由能力。本项目在 default 产品层把 Navigation 作为导航根容器通过 Provider 下发全局导航栈实现 Stack/Split 双模式切换与左右手持布局自适应。本文基于products/default/src/main/ets/view/Index.ets、features/multishortvideoindividual/src/main/ets/view/IndividualByRouter.ets与 route_map.json 展开。二、Navigation 容器与全局导航栈NavPathStack 在根组件创建通过 Provider 向子树共享子组件用 Consumer 拿到同一个栈对象Entry ComponentV2 struct Index { Provider(pathStack) pathStack: NavPathStack new NavPathStack(); Provider(showSideComment) showSideComment: boolean false; Provider(showSideIndividual) showSideIndividual: boolean false; build() { Navigation(this.pathStack) { MSVTabs({ data: this.data, ... }) } .navBarWidthRange([new WidthBreakpointTypenumber(410, 410, 700, 700).getValue(this.windowInfo.widthBp), 100%]) .navBarWidth(new WidthBreakpointTypenumber(410, 410, 700, 700).getValue(this.windowInfo.widthBp)) .hideBackButton(true) .hideTitleBar(true) .divider(null) .navBarPosition(this.holdingHandStatus motion.HoldingHandStatus.RIGHT_HAND_HELD ? NavBarPosition.Start : NavBarPosition.End) .mode(this.showSideComment || this.showSideIndividual ? NavigationMode.Split : NavigationMode.Stack) .enableModeChangeAnimation(false) } }要点navBarWidth 随断点变化XS/SM 用 410vpMD/LG 用 700vpnavBarPosition 跟随持握状态mode 由侧面板开关动态决定enableModeChangeAnimation(false) 防闪跳。三、NavDestination 与路由注册路由目标用 NavDestination 声明IndividualByRouter.etsBuilder export function IndividualByRouterBuilder() { IndividualByRouter() } ComponentV2 export struct IndividualByRouter { Consumer(pathStack) pathStack: NavPathStack new NavPathStack(); Consumer(showSideIndividual) showSideIndividual: boolean false; aboutToAppear(): void { this.pathStack.disableAnimation(this.windowInfo.widthBp WidthBreakpoint.WIDTH_MD ? false : true); } build() { NavDestination() { Individual() } .hideTitleBar(true) .hideBackButton(true) .onReady((context: NavDestinationContext) { this.pathStack context.pathStack; // 绑定页面级导航上下文 }) } }路由映射在模块resources/base/profile/route_map.json注册buildFunction 与导出的 Builder 同名{ routerMap: [ { name: IndividualByRouter, pageSourceFile: src/main/ets/view/IndividualByRouter.ets, buildFunction: IndividualByRouterBuilder } ] }default 产品层另注册了 SplitComment各产品模块各自维护 route_map.json路由互不干扰。四、入栈、出栈与参数传递视频页AdaptiveVideo.ets按断点选择跳转形态if (this.windowInfo.widthBp WidthBreakpoint.WIDTH_SM) { this.showSideComment true; // 大屏分栏侧开 this.pathStack.pushPathByName(SplitComment, null); // 入栈可携带参数 } else { this.showComment true; // 小屏半模态 } // 关闭侧面板 this.pathStack.pop(); this.showSideComment false;pushPathByName 的第二参可传任意对象目标页在 aboutToAppear/onReady 中读取onReady 回调返回 NavDestinationContext其中的 pathStack 支持页面内返回与结果回传。五、Stack/Split 双模式与多设备差异NavigationMode.Stack 全屏压栈Split 分栏时主内容区保持显示NavDestination 作为右侧面板展开。四种产品差异如下产品容器navBarWidth分栏策略defaultNavigation MSVTabs410/700vp 按断点评论、个人主页 SplitpcSideBarContainer 内嵌 Navigation66%同上tvNavigation TvTabs880vp同上wearableNavigation 精简 Tabs全宽始终 StackTV 端products/tv/src/main/ets/view/Index.ets同样以 Navigation 为根容器区别是页签用 TvTabs、navBarWidth 固定 880vp、无navBarPosition切换其余 Stack/Split 逻辑与 default 一致。六、点击主区关闭侧面板侧面板打开时主内容区拦截点击在 onGestureRecognizerJudgeBegin 中收起REJECT 吞掉点击防止误触播放.gesture(TapGesture()) .onGestureRecognizerJudgeBegin((event, current) { if (current (current.getType() GestureControl.GestureType.TAP_GESTURE || current.getType() GestureControl.GestureType.CLICK)) { if (this.showSideComment || this.showSideIndividual) { this.pathStack.pop(); this.showSideComment false; this.showSideIndividual false; return GestureJudgeResult.REJECT; } } return GestureJudgeResult.CONTINUE; })配合hitTestBehavior(HitTestMode.Block)被拦截的点击不会透传给视频播放层。七、总结与最佳实践NavPathStack 全局唯一用 Provider/Consumer 传递避免层层透传页面内导航用 onReady 的页面级栈。路由统一注册到 route_map.jsonbuildFunction 与导出 Builder 同名保证映射可维护。模式切换由业务开关驱动配合 enableModeChangeAnimation(false) 防止闪烁。大屏用 Split 分栏、小屏用半模态/Stack断点驱动形态统一收敛在跳转处。navBarPosition 跟随持握状态让折叠屏/平板单手场景更友好。