鸿蒙掌上驾考宝典应用开发3篇:鸿蒙应用路由框架——Navigation 深度解析
·
第3篇:鸿蒙应用路由框架——Navigation 深度解析

一、引言
在鸿蒙应用开发中,页面导航是核心基础能力。DriverLicenseExam 项目全面采用了 Navigation + NavPathStack 路由方案,替代了传统的页面路由(router)API。本文将从源码角度深度解析 Navigation 路由框架的设计与实现。
二、Navigation 路由架构
2.1 核心概念
Navigation 路由框架包含三个核心概念:
- NavPathStack:路由栈,管理页面入栈、出栈
- NavDestination:页面目的地,每个页面都是一个 NavDestination
- Navigation:容器组件,承载路由栈和页面
2.2 路由栈的创建
在项目中,路由栈通过 CommonModel 单例全局管理:
// commons/commonLib/src/main/ets/model/CommonModel.ets
export class CommonModel {
private static _instance: CommonModel;
public navStack: NavPathStack = new NavPathStack();
public static get instance(): CommonModel {
if (!CommonModel._instance) {
CommonModel._instance = new CommonModel();
}
return CommonModel._instance;
}
}
2.3 入口页面的路由配置
在 MainEntry 中,使用 Navigation 包裹整个应用:
// products/entry/src/main/ets/pages/MainEntry.ets
@Entry
@ComponentV2
struct MainEntry {
vm: CommonModel = CommonModel.instance;
build() {
Stack({ alignContent: Alignment.TopStart }) {
Navigation(this.vm.navStack) {
// 主页面内容(Tabs 底部导航)
Column() {
Tabs({ barPosition: BarPosition.End, index: this.vm.curIndex }) {
TabContent() { HomeView({...}); }
TabContent() { MineView({...}); }
}
}
}
.hideTitleBar(true)
.hideToolBar(true)
.hideBackButton(true)
.mode(NavigationMode.Stack)
}
}
}
三、路由注册与页面跳转
3.1 路由注册机制
在鸿蒙中,路由页面通过 @Builder 函数进行注册,框架会自动关联页面名称:
// 注册引导页路由
@Builder
export function GuidePageBuilder() {
GuidePage();
}
// 注册城市选择页路由
@Builder
export function SelectCityViewBuilder() {
SelectCityView();
}
// 注册练习页路由
@Builder
export function PracticeViewBuilder() {
PracticeView();
}
3.2 页面跳转
通过 NavPathStack 的 pushPathByName 方法跳转:
// 带参数跳转
this.vm.navStack.pushPathByName('practiceView', {
title: '顺序练习',
type: EXAM_MANAGER_TYPE.sequence
});
// 无参数跳转
this.vm.navStack.pushPathByName('selectCityView', true);
// 替换当前页面(不保留历史)
this.vm.navStack.replacePathByName('guidePage', true);
3.3 参数接收
目标页面在 NavDestination 的 onReady 回调中获取参数:
// PracticeView.ets 中接收参数
build() {
NavDestination() {
// 页面内容
}
.onReady((ctx: NavDestinationContext) => {
const index = this.vm.navStack.size();
const param: ROUTE_PARAM = this.vm.navStack.getParamByIndex(index - 1) as ROUTE_PARAM;
this.title = param.title;
this.type = param.type;
// 根据参数初始化数据
this.getExamManger();
})
}
四、路由参数类型设计
4.1 统一参数类型
项目定义了 ROUTE_PARAM 类型作为路由参数的标准格式:
// commons/datasource/src/main/ets/Model.ets
export interface ROUTE_PARAM {
title: string | Resource;
type: EXAM_MANAGER_TYPE;
examManager?: ExamManager;
questionId?: string;
wrongOrCollect?: WRONG_COLLECT;
keyword?: string;
}
4.2 多种导航场景
不同类型的导航使用不同的参数组合:
// 场景1:顺序练习 → 只需要 title + type
const param: ROUTE_PARAM = {
title: '顺序练习',
type: EXAM_MANAGER_TYPE.sequence
};
// 场景2:模拟考试 → 需要 examManager(包含完整试卷)
const param: ROUTE_PARAM = {
title: '模拟考试',
type: EXAM_MANAGER_TYPE.mock_exam,
examManager: this.examService.getMockExamManager('模拟考试')
};
// 场景3:错题本 → 需要 wrongOrCollect
const param: ROUTE_PARAM = {
title: '错题本',
type: EXAM_MANAGER_TYPE.error,
wrongOrCollect: WRONG_COLLECT.WRONG
};
// 场景4:搜索 → 需要 keyword
const param: ROUTE_PARAM = {
title: '搜索结果',
type: EXAM_MANAGER_TYPE.search,
keyword: '交通标志'
};
五、路由拦截与返回处理
5.1 返回拦截
模拟考试中,用户点击返回需要弹出确认弹窗,防止误操作:
// PracticeView.ets 返回拦截
.onBackPressed(() => {
if (this.type === EXAM_MANAGER_TYPE.mock_exam) {
// 打开模拟考试结束弹窗
this.examController.isShowMockExamDialog = true;
return true; // 拦截返回,不执行默认行为
}
this.vm.navStack.pop();
return true;
})
5.2 路由栈操作
RouterModule 封装了完整的路由栈操作:
// commons/commonLib/src/main/ets/utils/RouterModule.ets
export class RouterModule {
private static _stack: NavPathStack = new NavPathStack();
// 入栈
public static push(info: NavRouterInfo) {
RouterModule._stack.pushPathByName(info.url, info.param);
}
// 替换
public static replace(info: NavRouterInfo) {
RouterModule._stack.replacePathByName(info.url, info.param);
}
// 出栈
public static pop() {
RouterModule._stack.pop();
}
// 回退到指定页面
public static popToName(name: string) {
RouterModule._stack.popToName(name);
}
// 清空栈
public static clear() {
RouterModule._stack.clear();
}
// 获取栈大小
public static size() {
return RouterModule._stack.size();
}
}
六、全局弹窗管理
Navigation 路由框架还支持全局弹窗的管理,通过路由栈实现弹窗的打开/关闭:
// 打开全局弹窗
public static openDialog(name: string, param?: DialogInfo): void {
const indexArr = RouterModule._stack.getIndexByName(name);
if (indexArr.length) {
Logger.info(TAG, 'dialog already exists');
return;
}
RouterModule._stack.pushPath({
name: name,
param: param,
onPop: (data: PopInfo) => {
if (param?.onPop) param.onPop(data);
},
});
}
// 关闭全局弹窗
public static closeDialog(result?: DialogInfo) {
RouterModule._stack.pop(result);
}
七、路由参数传递的完整示例
以下是一个完整的路由跳转流程,从 HomeView 点击"顺序练习"到 PracticeView 渲染:
// 1. HomeView 发起跳转
this.vm.navStack.pushPathByName('practiceView', {
title: '顺序练习',
type: EXAM_MANAGER_TYPE.sequence
});
// 2. PracticeView 注册路由
@Builder
export function PracticeViewBuilder() {
PracticeView();
}
// 3. PracticeView 接收参数并初始化
@ComponentV2
export struct PracticeView {
@Local title: string | Resource = '顺序练习';
@Local type: EXAM_MANAGER_TYPE = EXAM_MANAGER_TYPE.sequence;
@Local examManager: ExamManager = new ExamManager(this.title, []);
build() {
NavDestination() {
Exam({
appPathStack: this.vm.navStack,
examManager: this.examManager,
});
}
.onReady(() => {
const index = this.vm.navStack.size();
const param = this.vm.navStack.getParamByIndex(index - 1) as ROUTE_PARAM;
this.title = param.title;
this.type = param.type;
if (param.examManager) {
this.examManager = param.examManager;
return;
}
this.getExamManger();
})
}
}
八、总结
Navigation 路由框架相比传统 router API 的优势:
| 特性 | Navigation | 传统 router |
|---|---|---|
| 页面栈管理 | 原生支持 | 需手动维护 |
| 参数传递 | 类型安全 | 序列化传递 |
| 返回拦截 | 内置支持 | 需额外处理 |
| 全局弹窗 | 路由栈管理 | 无原生支持 |
| 自定义动画 | 丰富 | 有限 |
在 DriverLicenseExam 项目中,Navigation 路由框架支撑了引导页、考试页、设置页等数十个页面之间的流畅导航,是鸿蒙应用开发中推荐使用的路由方案。
关键源码文件:
products/entry/src/main/ets/pages/MainEntry.ets— 入口路由commons/commonLib/src/main/ets/utils/RouterModule.ets— 路由封装commons/commonLib/src/main/ets/model/CommonModel.ets— 路由栈管理products/entry/src/main/ets/pages/practice/PracticeView.ets— 路由参数接收示例
更多推荐




所有评论(0)