第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 — 路由参数接收示例
Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐