HarmonyOS HMRouter 接入记录:从普通 Tab Demo 到路由跳转

HarmonyOS HMRouter 接入记录:从普通 Tab Demo 到路由跳转

前言

今天在鸿蒙 Demo 中接入了 @hadss/hmrouter 路由库。

一开始只是用 @Local currentIndex 控制底部 Tab 切换:

txt 复制代码
点击底部按钮
↓
修改 currentIndex
↓
if / else 展示不同组件

这种方式本质是组件切换,不是页面路由。

后来改成使用 HMRouter:

txt 复制代码
点击底部按钮
↓
HMRouterMgr.push()
↓
通过路由切换页面

这篇文章记录完整接入流程,以及遇到的问题,方便以后排查。


一、最终实现目标

Demo 有三个页面:

txt 复制代码
首页
Home
Setting

底部有一个导航栏,点击不同 Tab 时,通过 HMRouter 路由跳转到对应页面。

最终结构:

txt 复制代码
Index.ets
│
├── HMNavigation 路由容器
│
└── 底部 TabBar
     ├── 首页 -> pages/TabHome
     ├── Home -> pages/Home
     └── Setting -> pages/Setting

二、目录结构

示例目录:

txt 复制代码
MyApplication
├── AppScope
│   └── app.json5
├── entry
│   ├── hvigorfile.ts
│   ├── hmrouter_config.json
│   └── src
│       └── main
│           ├── ets
│           │   ├── entryability
│           │   │   └── EntryAbility.ets
│           │   └── pages
│           │       ├── Index.ets
│           │       ├── TabHome.ets
│           │       ├── Home.ets
│           │       └── Setting.ets
│           └── module.json5
├── hvigor
│   └── hvigor-config.json5
├── hvigorfile.ts
└── oh-package.json5

三、接入 HMRouter 的完整流程

HMRouter 接入主要分 8 步:

txt 复制代码
1. 安装运行库
2. 配置 hvigor 插件依赖
3. 在 entry/hvigorfile.ts 启用插件
4. 新建 hmrouter_config.json 指定扫描目录
5. EntryAbility 初始化 HMRouterMgr
6. 页面使用 @HMRouter 注册
7. Index 使用 HMNavigation 承载路由页面
8. 点击底部 Tab 使用 HMRouterMgr.push 跳转

四、第一步:安装运行库

在项目根目录执行:

bash 复制代码
ohpm install @hadss/hmrouter

这个包提供运行时代码:

ts 复制代码
HMNavigation
HMRouter
HMRouterMgr

如果没有安装,页面里 import 会失败。


五、第二步:配置插件依赖

打开:

txt 复制代码
hvigor/hvigor-config.json5

添加:

json5 复制代码
{
  "modelVersion": "6.1.0",
  "dependencies": {
    "@hadss/hmrouter-plugin": "^1.2.2"
  }
}

注意:

txt 复制代码
@hadss/hmrouter 是运行库,用 ohpm 安装
@hadss/hmrouter-plugin 是编译插件,写在 hvigor-config.json5

不要执行:

bash 复制代码
ohpm install @hadss/hmrouter-plugin

否则可能 404。


六、第三步:entry/hvigorfile.ts 启用插件

打开:

txt 复制代码
entry/hvigorfile.ts

写:

ts 复制代码
import { hapTasks } from '@ohos/hvigor-ohos-plugin'
import { hapPlugin } from '@hadss/hmrouter-plugin'

export default {
  system: hapTasks,
  plugins: [
    hapPlugin()
  ]
}

这一步非常关键。

如果没有启用插件,@HMRouter 不会被扫描,也不会生成路由表。


七、第四步:新建 hmrouter_config.json

entry 目录下新建:

txt 复制代码
entry/hmrouter_config.json

内容:

json 复制代码
{
  "scanDir": ["src/main/ets/pages"],
  "saveGeneratedFile": true
}

作用:

txt 复制代码
告诉 HMRouter 插件去哪里扫描 @HMRouter 页面

因为页面在:

txt 复制代码
entry/src/main/ets/pages

所以 scanDir 写:

txt 复制代码
src/main/ets/pages

注意这个文件位置:

txt 复制代码
entry/hmrouter_config.json

不是:

txt 复制代码
项目根目录/hmrouter_config.json

八、第五步:EntryAbility 初始化 HMRouterMgr

打开:

txt 复制代码
entry/src/main/ets/entryability/EntryAbility.ets

引入:

ts 复制代码
import { HMRouterMgr } from '@hadss/hmrouter'

onCreate 中初始化:

ts 复制代码
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
  HMRouterMgr.openLog('INFO')
  HMRouterMgr.init({
    context: this.context
  })
}

如果没有初始化,运行时可能报:

txt 复制代码
ERR_INIT_NOT_READY
Framework initialization not completed

九、第六步:页面使用 @HMRouter 注册

TabHome.ets

ts 复制代码
import { HMRouter } from '@hadss/hmrouter'

@HMRouter({ pageUrl: 'pages/TabHome' })
@ComponentV2
export struct TabHome {
  build() {
    Column() {
      Text('首页')
        .fontSize(30)
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
    .alignItems(HorizontalAlign.Center)
  }
}

Home.ets

ts 复制代码
import { HMRouter } from '@hadss/hmrouter'

@HMRouter({ pageUrl: 'pages/Home' })
@ComponentV2
export struct HomeBuilder {
  build() {
    Column() {
      Text('Home 页面')
        .fontSize(30)
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
    .alignItems(HorizontalAlign.Center)
  }
}

Setting.ets

ts 复制代码
import { HMRouter } from '@hadss/hmrouter'

@HMRouter({ pageUrl: 'pages/Setting' })
@ComponentV2
export struct Setting {
  build() {
    Column() {
      Text('Setting 页面')
        .fontSize(30)
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
    .alignItems(HorizontalAlign.Center)
  }
}

十、第七步:Index.ets 使用 HMNavigation

Index.ets 是入口页面。

ts 复制代码
import { HMNavigation, HMRouterMgr } from '@hadss/hmrouter'

@Entry
@ComponentV2
struct Index {
  @Local currentTab: string = 'pages/TabHome'

  build() {
    Column() {
      HMNavigation({
        navigationId: 'MainNavigation',
        homePageUrl: 'pages/TabHome'
      })
      .layoutWeight(1)
      .width('100%')

      Row() {
        this.TabItem('首页', 'pages/TabHome')
        this.TabItem('Home', 'pages/Home')
        this.TabItem('Setting', 'pages/Setting')
      }
      .width('100%')
      .height(60)
      .backgroundColor('#FFFFFF')
    }
    .width('100%')
    .height('100%')
  }

  @Builder
  TabItem(title: string, pageUrl: string) {
    Column() {
      Text(title)
        .fontSize(14)
        .fontColor(this.currentTab === pageUrl ? '#007DFF' : '#666666')
    }
    .layoutWeight(1)
    .height('100%')
    .justifyContent(FlexAlign.Center)
    .alignItems(HorizontalAlign.Center)
    .onClick(() => {
      this.currentTab = pageUrl
      HMRouterMgr.push({
        pageUrl: pageUrl
      })
    })
  }
}

十一、整体流程图

txt 复制代码
应用启动
│
▼
EntryAbility.onCreate()
│
├── HMRouterMgr.openLog()
└── HMRouterMgr.init()
        │
        ▼
Index.ets
│
├── HMNavigation
│     └── homePageUrl: pages/TabHome
│
└── 底部 TabBar
      │
      ├── 点击 首页
      │     └── HMRouterMgr.push({ pageUrl: 'pages/TabHome' })
      │
      ├── 点击 Home
      │     └── HMRouterMgr.push({ pageUrl: 'pages/Home' })
      │
      └── 点击 Setting
            └── HMRouterMgr.push({ pageUrl: 'pages/Setting' })

十二、每一步的作用总结

步骤 作用
ohpm install @hadss/hmrouter 安装运行库
hvigor-config.json5 配置 plugin 让 hvigor 能下载编译插件
entry/hvigorfile.ts 启用 hapPlugin() 让插件真正执行
entry/hmrouter_config.json 指定扫描页面目录
EntryAbility.ets 初始化 初始化路由框架
@HMRouter 注册页面
HMNavigation 路由容器
HMRouterMgr.push 跳转页面

十三、常见问题记录

1. ERR_INIT_NOT_READY

错误:

txt 复制代码
[HMRouter ERROR] ERR_INIT_NOT_READY 40001003
Framework initialization not completed

原因:

txt 复制代码
HMRouterMgr 没有初始化

解决:

EntryAbility.etsonCreate 中添加:

ts 复制代码
HMRouterMgr.openLog('INFO')
HMRouterMgr.init({
  context: this.context
})

2. not exist pageUrl in store

错误:

txt 复制代码
[HMRouter INFO][HMRouterStore] not exist pageUrl in store, pageUrl is pages/Home

原因:

txt 复制代码
路由表里没有这个页面

常见原因:

txt 复制代码
1. 页面没有写 @HMRouter
2. pageUrl 不一致
3. hmrouter-plugin 没有执行
4. scanDir 没覆盖页面目录
5. 没有 Clean / Rebuild
6. 在 Previewer 里运行

解决排查:

txt 复制代码
1. 确认页面有 @HMRouter({ pageUrl: 'pages/Home' })
2. 确认 push 的 pageUrl 完全一致
3. 确认 entry/hvigorfile.ts 配置 hapPlugin()
4. 确认 entry/hmrouter_config.json 位置正确
5. Clean Project + Rebuild Project
6. Run 到模拟器,不要只用 Previewer

3. 搜不到 hm_router_map.json

如果搜不到生成的路由表,说明插件没有成功生成路由映射。

排查:

txt 复制代码
1. hmrouter_config.json 是否在 entry 目录下
2. scanDir 是否写对
3. entry/hvigorfile.ts 是否启用 hapPlugin()
4. 是否 Rebuild Project

可以在 DevEco 中:

txt 复制代码
Ctrl + Shift + F

搜索:

txt 复制代码
pages/Home

如果生成成功,除了自己写的页面代码外,还应该能搜到生成文件中的路由信息。


4. Previewer 运行异常

日志中如果出现:

txt 复制代码
previewer is a mocked implementation

说明当前是在 Previewer 中运行。

HMRouter 依赖编译产物、rawfile 和运行时能力,建议:

txt 复制代码
不要用 Previewer 验证 HMRouter
要 Run 到模拟器或真机

5. 安装失败:install sign info inconsistent

错误:

txt 复制代码
Install Failed: error: failed to install bundle.
code:9568332
error: install sign info inconsistent.

原因:

txt 复制代码
模拟器里有旧包残留
旧包签名和新包签名不一致

解决:

先查设备:

powershell 复制代码
& "C:\Program Files\Huawei\DevEco Studio\sdk\default\openharmony\toolchains\hdc.exe" list targets

输出类似:

txt 复制代码
127.0.0.1:5555

卸载旧包:

powershell 复制代码
& "C:\Program Files\Huawei\DevEco Studio\sdk\default\openharmony\toolchains\hdc.exe" -t 127.0.0.1:5555 uninstall com.example.myapplication

然后重新运行。


十四、调试经验总结

这次接入 HMRouter 之后,发现鸿蒙路由库的问题不一定是页面代码问题。

更多时候问题出在:

txt 复制代码
1. 编译插件没有执行
2. 路由表没有生成
3. pageUrl 不一致
4. 没有初始化路由框架
5. 使用 Previewer 而不是模拟器
6. 模拟器旧包签名冲突

所以以后遇到白屏,优先看:

txt 复制代码
HiLog 里的 HMRouter 日志
Build Output 里的插件执行日志
是否生成 hm_router_map.json
是否 Run 到模拟器

十五、最终理解

HMRouter 使用起来其实不复杂,真正复杂的是第一次接入时的工程配置。

可以简单记成一句话:

txt 复制代码
ohpm 装运行库
hvigor 配插件
hmrouter_config 指定扫描目录
EntryAbility 初始化
@HMRouter 注册页面
HMNavigation 承载页面
HMRouterMgr.push 跳转页面

跑通一次之后,后面基本就是复制模板。


十六、组件切换 vs 路由跳转

组件切换

txt 复制代码
@Local currentIndex
↓
if / else
↓
显示不同组件

优点:

txt 复制代码
简单,适合练习状态和组件通信

缺点:

txt 复制代码
不是真正页面路由
没有路由栈
不适合复杂页面跳转

HMRouter 路由跳转

txt 复制代码
@HMRouter 注册页面
↓
HMNavigation 承载
↓
HMRouterMgr.push 跳转

优点:

txt 复制代码
是真正页面路由
支持页面栈
适合企业项目

十七、后续计划

下一步可以继续练:

txt 复制代码
1. 路由传参
2. HMRouterMgr.pop 返回
3. replace 跳转
4. 页面生命周期
5. 路由拦截
6. 结合商品管理 Demo 做详情页

比如:

txt 复制代码
商品列表页
↓
点击商品
↓
路由跳转到商品详情页
↓
传递商品 id

这样更接近真实业务。

相关推荐
木斯佳4 小时前
前端八股文面经大全:腾讯WXG暑期前端一面(2026-05-15)·面经深度解析
前端·面试·笔试
canonical_entropy4 小时前
NOP Chaos Flux 架构演变史:从 AMIS 重写到现代低代码运行时
前端·aigc·ai编程
张元清5 小时前
useEffect 之外:专门处理异步、深比较和 SSR 的 Effect Hook
前端·javascript·面试
小小小小宇5 小时前
前端双Token机制无感刷新(二)
前端
zhangxingchao6 小时前
AI Agent 基础问题系统整理:从 LangChain、LangGraph、MCP 到 Agent 架构、记忆、工具调用与评估体系
前端·人工智能·后端
Moment6 小时前
AI 为什么总喜欢写防御性代码?
前端·后端·面试
浑手营销6 小时前
浑手科技案例分享:133个精准询盘短视频玩法
前端·人工智能·科技
IT_陈寒6 小时前
SpringBoot自动配置的坑,差点让我加班到天亮
前端·人工智能·后端
LucianaiB6 小时前
【Dify + EdgeOne】你奶奶也会做一个“智票通”,轻松票据自定义提取+防数据泄露
前端·后端