17 — Navigation 路由与 NavPathStack

一、引言

breakpoint-system

短视频应用中"视频页 → 评论区 / 个人主页"的跳转高频且要求无闪烁。HarmonyOS 的 Navigation 配合 NavPathStack 提供声明式路由能力。本项目在 default 产品层把 Navigation 作为导航根容器,通过 @Provider 下发全局导航栈,实现 Stack/Split 双模式切换与左右手持布局自适应。本文基于 products/default/src/main/ets/view/Index.etsfeatures/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 WidthBreakpointType<number>(410, 410, 700, 700).getValue(this.windowInfo.widthBp), '100%'])
    .navBarWidth(new WidthBreakpointType<number>(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 用 410vp,MD/LG 用 700vp);navBarPosition 跟随持握状态;mode 由侧面板开关动态决定,enableModeChangeAnimation(false) 防闪跳。

三、NavDestination 与路由注册

路由目标用 NavDestination 声明(IndividualByRouter.ets):

@Builder 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 按断点评论、个人主页 Split
pcSideBarContainer 内嵌 Navigation'66%'同上
tvNavigation + TvTabs880vp同上
wearableNavigation + 精简 Tabs全宽始终 Stack

TV 端(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.json,buildFunction 与导出 @Builder 同名,保证映射可维护。
  • 模式切换由业务开关驱动,配合 enableModeChangeAnimation(false) 防止闪烁。
  • 大屏用 Split 分栏、小屏用半模态/Stack,断点驱动形态统一收敛在跳转处。
  • navBarPosition 跟随持握状态,让折叠屏/平板单手场景更友好。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐