CHAPTER 04 · 10

导航与路由

掌握 Navigation 组件、页面栈管理、转场动画与深链接,构建多页面 HarmonyOS 应用的核心能力。

HarmonyOS NEXT 的导航体系

HarmonyOS NEXT 提供了两套导航方案,适合不同场景:

Navigation 组件(推荐)
ArkUI 原生导航容器,支持自适应单/双列布局(手机显示单列,平板自动变双列)、NavDestination 页面管理、转场动画、路由拦截。这是 HarmonyOS NEXT 官方推荐的主导航方案,适合绝大多数应用。
Router 模块(传统方案)
@ohos.router 提供的命令式路由 API,通过 router.pushUrl()、router.back() 控制页面栈。语法更简单,但功能不如 Navigation 丰富,不支持自适应布局。适合简单应用或旧代码迁移。

Navigation 组件

Navigation 是一个智能导航容器,它会根据设备屏幕大小自动决定显示方式:手机显示单页模式(Stack),平板/折叠屏展开后显示双栏模式(Split),开发者无需为不同设备单独适配。

基本结构

// NavPathStack 是页面栈的控制器,负责 push/pop/replace 操作
// 通常在根组件中创建,向下传递
@Entry
@Component
struct MainPage {
  // NavPathStack:页面路由栈,管理所有 NavDestination 页面
  navStack: NavPathStack = new NavPathStack()

  build() {
    // Navigation 组件包裹整个应用导航区域
    Navigation(this.navStack) {
      // 这里是"主页"内容,也是 Navigation 的首屏
      HomeContent({ navStack: this.navStack })
    }
    // 设置标题栏模式
    .title('首页')
    // 隐藏返回按钮(首页不需要)
    .hideBackButton(true)
    // 设置导航模式:Auto 自适应(手机Stack/平板Split)
    .mode(NavigationMode.Auto)
    // 注册路由表:key → 对应的 NavDestination 组件
    .navDestination(this.PageMap)
  }

  // @Builder 定义页面路由映射表
  @Builder
  PageMap(name: string, param: object) {
    if (name === 'ArticleDetail') {
      ArticleDetailPage({ param: param as ArticleParam })
    } else if (name === 'UserProfile') {
      UserProfilePage({ param: param as UserParam })
    } else if (name === 'Settings') {
      SettingsPage()
    }
  }
}

页面跳转与传参

// 定义参数类型(强烈推荐定义接口,保证类型安全)
interface ArticleParam {
  articleId: number
  title: string
  source: string
}

// 首页组件:触发页面跳转
@Component
struct HomeContent {
  navStack: NavPathStack = new NavPathStack()

  build() {
    Column() {
      Button('查看文章详情')
        .onClick(() => {
          // pushPathByName:跳转到命名路由,传递参数
          this.navStack.pushPathByName('ArticleDetail', {
            articleId: 42,
            title: 'ArkUI 深度解析',
            source: 'home'
          } as ArticleParam)
        })

      Button('个人主页')
        .onClick(() => {
          this.navStack.pushPathByName('UserProfile', { userId: 1001 })
        })

      Button('替换当前页(不留历史)')
        .onClick(() => {
          // replacePathByName:替换当前页,不产生历史记录
          this.navStack.replacePathByName('Settings', {})
        })
    }
  }
}

// 详情页:接收参数
@Component
struct ArticleDetailPage {
  param: ArticleParam = { articleId: 0, title: '', source: '' }

  // @Consume 获取 Navigation 注入的路由栈(在子页面中使用)
  pathStack: NavPathStack = new NavPathStack()

  build() {
    // NavDestination 是每个子页面的根容器
    NavDestination() {
      Column({ space: 16 }) {
        Text(this.param.title).fontSize(24).fontWeight(FontWeight.Bold)
        Text(`文章 ID:${this.param.articleId}`)

        Button('返回')
          .onClick(() => {
            this.pathStack.pop()          // 普通返回
            // this.pathStack.popToName('Home')  // 返回到指定页面
            // this.pathStack.clear()             // 清空历史
          })
      }
      .padding(24)
    }
    .title(this.param.title)
  }
}

底部 Tab 导航

大多数应用都有底部 Tab 栏。在 HarmonyOS NEXT 中,可以用 Tabs 组件或在 Navigation 内部嵌套实现:

@Entry
@Component
struct MainTabsPage {
  @State currentTab: number = 0
  private tabsController: TabsController = new TabsController()

  build() {
    Tabs({
      barPosition: BarPosition.End,   // 底部 Tab 栏
      controller: this.tabsController,
      index: this.currentTab
    }) {
      // 每个 TabContent 对应一个 Tab 页
      TabContent() {
        HomePage()
      }
      .tabBar(this.TabBarItem('首页', '🏠', 0))

      TabContent() {
        DiscoverPage()
      }
      .tabBar(this.TabBarItem('发现', '🔍', 1))

      TabContent() {
        ProfilePage()
      }
      .tabBar(this.TabBarItem('我的', '👤', 2))
    }
    .onChange((index: number) => {
      this.currentTab = index
    })
    .barMode(BarMode.Fixed)   // Tab 均分宽度
  }

  // 自定义 TabBar 图标样式
  @Builder
  TabBarItem(label: string, icon: string, index: number) {
    Column({ space: 4 }) {
      Text(icon).fontSize(22)
      Text(label)
        .fontSize(11)
        .fontColor(this.currentTab === index ? '#CF0A2C' : '#768390')
    }
    .padding({ top: 8, bottom: 8 })
  }
}

页面转场动画

HarmonyOS NEXT 提供了丰富的页面转场动画,既有系统预设,也支持完全自定义:

// NavDestination 内设置转场动画
NavDestination() {
  // 页面内容
}
.customNavContentTransition(this.customTransition)

// 自定义转场:从右侧滑入
customTransition(
  from: NavContentInfo,
  to: NavContentInfo,
  operation: NavigationOperation
): NavigationAnimatedTransition | undefined {
  if (operation === NavigationOperation.PUSH) {
    return {
      onTransitionEnd: (isSuccess: boolean) => {
        console.log('转场完成', isSuccess)
      },
      timeout: 1000,
      transition: (transitionProxy: NavigationTransitionProxy) => {
        // 使用 animateTo 控制动画
        animateTo({
          duration: 350,
          curve: Curve.EaseOut
        }, () => {
          transitionProxy.finishTransition()
        })
      }
    }
  }
  return undefined  // 使用默认转场
}

// 组件内动画(进入/退出)
Column()
  .transition(
    TransitionEffect.OPACITY.combine(
      TransitionEffect.translate({ x: 100 })
    )
    .animation({ duration: 300, curve: Curve.EaseOut })
  )

深链接与 Want

深链接(Deep Link)允许外部应用或网页直接跳转到应用内的特定页面。在 HarmonyOS NEXT 中,通过配置 Want 的 URI 和 Action 实现:

// module.json5 中配置深链接
// "skills" 字段声明 Ability 响应的 URI
{
  "skills": [{
    "entities": ["entity.system.home"],
    "actions": ["ohos.want.action.viewData"],
    "uris": [{
      "scheme": "myapp",
      "host": "article",
      "pathStartWith": "/"
    }]
  }]
}

// EntryAbility.ts — 处理深链接跳转
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {
  this.handleWant(want)
}

// 应用已在前台时收到新 Want
onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam) {
  this.handleWant(want)
}

handleWant(want: Want) {
  const uri = want.uri  // e.g. "myapp://article/123"
  if (uri) {
    const url = new URL(uri)
    const articleId = url.pathname.replace('/', '')

    // 通过 AppStorage 传递跳转目标,让页面组件监听并跳转
    AppStorage.setOrCreate('deepLinkTarget', {
      page: 'ArticleDetail',
      params: { articleId: Number(articleId) }
    })
  }
}

路由拦截与守卫

类似 Vue Router 的 beforeEach 守卫,Navigation 支持在页面跳转前执行拦截逻辑,常用于登录态检验:

Navigation(this.navStack) {
  HomeContent()
}
.navDestination(this.PageMap)
// 路由拦截:跳转前执行检查
.onNavBarStateChange((isVisible: boolean) => {
  console.log('导航栏可见性变化:', isVisible)
})
// 监听路由栈变化
.onNavigationModeChange((mode: NavigationMode) => {
  console.log('导航模式变化(单/双栏切换):', mode)
})

// 在跳转时手动拦截
function navigateWithAuth(
  navStack: NavPathStack,
  pageName: string,
  params: object
) {
  const isLoggedIn = AppStorage.get<boolean>('isLoggedIn')
  const needsAuth = ['UserProfile', 'Orders', 'Checkout']

  if (needsAuth.includes(pageName) && !isLoggedIn) {
    // 未登录跳转到登录页,带上原目标作为重定向参数
    navStack.pushPathByName('Login', {
      redirect: pageName,
      redirectParams: params
    })
    return
  }
  navStack.pushPathByName(pageName, params)
}
Navigation vs Router 如何选择 新项目统一使用 Navigation + NavDestination 方案。它支持多设备自适应、转场动画定制、路由拦截,且与华为未来的多窗口特性兼容。Router 模块虽然简单,但不支持自适应布局,在折叠屏和平板上体验差。老项目可以逐步将 Router 迁移到 Navigation。