当前位置: 首页 > news >正文

从零开始开发纯血鸿蒙应用之跨模块路由

跨模块路由

  • 〇、前言
  • 一、设计思路
    • 1、支撑模块化开发
    • 2、支持路由传参
    • 3、模块解耦
  • 二、API 选型
    • 1、import 动态加载
    • 2、NavPathStack 路由栈
    • 3、wrapBuilder 全局封装
  • 三、实现路由模块
    • 1、定义 RouterInfo
    • 2、定义路由注册器
    • 3、定义 EasyRouter
      • 3.1、RegisterRoutePage
      • 3.2、back
      • 3.3、pushUrl
  • 四、体验跨模块路由
    • 1、创建 feature_test 模块
    • 2、配置路由
    • 3、注册路由
    • 4、设置 Navigation 组件
    • 5、定义 RouteParam
    • 6、使用路由
      • 6.1、跳转页面并传入数据
      • 6.2、读取数据并展示
      • 6.3、页面返回
  • 五、总结

在开始本章内容之前,先说一些题外话,就是之前利用 ohpm 公开发布到中心仓库的 lib_log 已经通过了审核,现在已经可以直接利用 ohpm 进行安装。

在这里插入图片描述

〇、前言

如果你也是紧跟 API 版本去升级 DevEco Studio 的人,那么你就会发现在 2025 年 7 月 4 日发布的 DevEco Studio 5.1.0 Release 中,又有很多 API 用法被 deprecated,比如 promptAction.showToast,也比如 router.pushUrl。

对于 router 模块,如果你打开最新的 API 文档,就会发现它一整个都被 deprecated 掉了:
在这里插入图片描述
当然了,对于直接用 Navigation 组件重构应用成本很高的项目来说,还可以折中使用下面的方式:
在这里插入图片描述
也就是下面这样:
在这里插入图片描述
但这总归是缓兵之计,并非长治久安之策,稳妥的方式还是使用 Navigation 组件实现自定义路由功能,这也是本文所要向大家介绍的。

一、设计思路

直接使用 router 模块,或者是从 UIContext 中获取 Router 实例对象,有个很明显的弊端,就是只能路由那些使用了 @Entry 装饰的 Page Component,使用限制比较大,现在,既然官方开始废弃 router 模块了,那么,也该是时候采用 Navigation 组件设计自定义路由了。

1、支撑模块化开发

既然要搞自定义路由,那么就必须实现成充分支持模块化开发的,也就是所有页面可以根据复杂程度和从属关系,或独自成模块或搭配成模块,也就是新的路由功能要支持跨模块的页面跳转。

2、支持路由传参

路由页面的同时,传入目标页面需要的数据,是应用开发过程中的典型场景,一个称得上好用的路由模块,就必须支持路由传参。

3、模块解耦

考虑到新开发的路由模块,会像 lib_log 一样用 ohpm 公开发布,那么该路由模块里面就不能有本地依赖,只能使用同样存在于 ohpm 中心仓的依赖,否则就无法迁移到其他项目中使用。

二、API 选型

要实现上述设计思想,那么,就需要在鸿蒙SDK 中选择合适的 API 或者 API 特性,根据我的实践,最终选择了如下内容:

  • import 的动态加载特性
  • NavPathStack 路由栈
  • wrapBuilder 全局封装

1、import 动态加载

在 ArkTS 脚本开头处所编写的 import 语句,实际上,属于 import 的静态加载,而这也是 import 函数的常规用法,而 import 函数的高级用法,便是动态加载

import 动态加载的实现代码,类似如下:

import(moduleName).then((result: ESObject) => {// 成功加载模块后的处理代码}, (error: ESObject) => {// 加载失败的处理代码}) ;

没错,import 动态加载的实质,就是异步回调。动态import能够支持的形式有如下:
在这里插入图片描述
特别注意的:

DevEco Studio中模块间的依赖关系通过oh-package.json5中的dependencies进行配置。dependencies列表中所有模块默认都会进行安装(本地模块)或下载(远程模块),但是不会默认参与编译。HAP/HSP编译时会以入口文件(一般为Index.ets/ts)开始搜索依赖关系,搜索到的模块或文件才会加入编译。
在编译期,静态import和常量动态import可以被打包工具rollup及其插件识别解析,加入依赖树中,参与编译流程,最终生成方舟字节码。但是,如果是变量动态import,该变量值可能需要进行运算或外部传入才能得到,在编译态无法解析其内容,也就无法加入编译。为了将这部分模块/文件加入编译,还需要额外增加一个runtimeOnly的buildOption配置,用于配置动态import的变量实际的模块名或文件路径。

2、NavPathStack 路由栈

NavPathStack 并不是一个独立的 API 模块,而是 Navigation 组件的一部分:
在这里插入图片描述
它是 Navigation 组件实现页面路由的核心,也是我们实现自定义路由管理功能的基础。

路由栈也是栈,是栈就能进栈和出栈;NavPathStack 的进栈方法,这里主要选用:
在这里插入图片描述
而利用 pushPath 方法的第一个参数,即 NavPathInfo 我们可以轻松实现路由传参功能:
在这里插入图片描述
而对应的页面返回,实际上就是出栈操作,也就是 pop 方法,此外还可以利用清栈 clear 方法,去实现直接回到应用起始页。

3、wrapBuilder 全局封装

在这里插入图片描述
wrapBuilder 接口,实际上是为了配合动态import,去实现跨模块加载页面的实现代码:
在这里插入图片描述
wrapBuilder 接口的使用必须遵循如下限制:
1、wrapBuilder方法只能传入全局@Builder方法。
2、wrapBuilder方法返回的WrappedBuilder对象的builder属性方法只能在struct内部使用。

三、实现路由模块

1、定义 RouterInfo

由于页面分散在不同模块中,页面名称也各不相同,页面 url 更是各自独一份,所有的这些构成路由信息,必须用适当的对象进行跟踪,因此,我使用如下代码定义了一个 RouterInfo:
在这里插入图片描述

2、定义路由注册器

基于 RouterInfo,定义一个 RouterRegister,从而方便进行动态的路由注册,具体实现代码如下:
在这里插入图片描述
在这个 RouterRegister 里面,用一个私有的 RouterInfo 数组去记录每一个所注册的 RouterInfo,同时基于该 RouterInfo 数组提供一些查找方法,比如根据 url 查找完整的 RouterInfo。

3、定义 EasyRouter

这一步是整个路由模块的核心,因为 EasyRouter 承担着实际的路由管理,它的实现代码基本如下:
在这里插入图片描述
考虑文章篇幅,这里只重点讲解 RegisterRoutePage、pushUrl 和 back 方法

3.1、RegisterRoutePage

这个方法,实际上就在上面讲解 wrapBuilder 的时候出镜过,它的具体实现如下:
在这里插入图片描述
通过该方法,可以传入一个包含具体页面实现代码的全局 builder,而该全局 builder 又会通过 registerBuilder 方法,添加到 EasyRouter 的 builderMap 中,从而令 Navigation 组件获得目标页面的“房间平面图”和“装修方案”。

3.2、back

由于 pushUrl 方法的代码更为复杂,所以,遵循由简入繁的叙述顺序,先讲解 back 方法的实现:
在这里插入图片描述

进行页面返回的时候,只需对路由栈进行出栈操作,而记录着页面具体构建方式的 builderMap 是不用进行清理的,因为被返回的页面还会第二次打开,而一旦对 builderMap 进行清理,那么下一次打开相同页面,Navigation 组件就不知道要怎么渲染该页面了。

退栈的时候,需要判断一下是否还能进行退栈操作,避免过度退栈引发程序奔溃。

3.3、pushUrl

pushUrl 方法,核心就是使用动态加载也即动态import,将目标页面从目标模块从加载出来:
在这里插入图片描述
当目标模块被成功加载进来后,将相应的路由信息添加到路由栈,也即 Navigation 的 NavPathStack 中,最后记录当前引用的页面,即更新 EasyRouter.referrer 数组。

详细的 EasyRouter 代码,可以访问EasyRouter 进行浏览。

四、体验跨模块路由

接下来,就可以使用上面的路由模块,进行跨模块路由的体验了。

1、创建 feature_test 模块

这个模块提供一个目标页:
在这里插入图片描述
并且,配合动态import,在 feature_test 模块入口文件Index.ets 文件中,定义一个 harInit 方法:

export function harInit(name: string) {switch(name) {case RouterRegister.getPageNameByUrl("/feature_test/main_page"):import("./src/main/ets/components/MainPage");Logger.info(`${RouterRegister.getPageNameByUrl("/feature_test/main_page")}被动态加载`, '[feature_test:harInit]')break;default:break;}
}

由于 Navigation 组件放在 entry 模块中,所以,feature_test 模块需要在 entry 模块中进行 dependences:
在这里插入图片描述
并在build-profile.json5 文件中,相应设置 runtimeOnly 选项:
在这里插入图片描述

2、配置路由

其次,需要对路由进行配置,也就是注册路由信息。
对于路由信息的注册,可以考虑创建一个专门放置只读常量的模块,并在下面的代码放在合适文件中:

import { RouterInfo } from "lib_easyrouter";export class RouterInfoConstant {static readonly FeatureTestMainPage: RouterInfo =new RouterInfo("/feature_test/main_page", "FeatureTestMainPage", "feature_test");
}

我这里,只是为了演示跨模块路由,所以,上述代码是放在 entry 模块下。

3、注册路由

接下来,利用 RouterInfoConstant 将具体的路由信息,注册到 RouterRegister 中,我这里是使用一个 EntryUtil 来实现的:
在这里插入图片描述
EntryUtil.initRouterInfoRegister 方法,会在 EntryAbility 的 onCreate 方法中进行调用:
在这里插入图片描述

4、设置 Navigation 组件

接下来,需要结合 EasyRouter 去设置 Navigation 组件,而 Navigation 组件,我是直接放在 entry 模块自动创建的 Index 页面中:
在这里插入图片描述

5、定义 RouteParam

为了方便体验通过路由进行数据传递,需要定义一个 RouteParam 对象,这是因为我目前只对对象类型的数据做了路由传参支持:
在这里插入图片描述

6、使用路由

6.1、跳转页面并传入数据

在 Navigation 组件中,简单的使用 Column 组件添加一个按钮:
在这里插入图片描述

6.2、读取数据并展示

在 feature_test 模块的目标页面,通过 aboutToAppear 函数,将数据从路由栈中取出:
在这里插入图片描述

6.3、页面返回

页面返回比较简单,就是用一个按钮去触发 EasyRouter.back()

五、总结

经过本篇的学习,我相信屏幕前的你,不仅学会了 import 的另一种使用方式,还学会使用动态 import 结合 Navigation 的 NavPathStack 实现跨模块的页面路由。

http://www.lryc.cn/news/593986.html

相关文章:

  • OpenCV 入门知识:图片展示、摄像头捕获、控制鼠标及其 Trackbar(滑动条)生成!
  • Ubuntu 24.04 设置静态 IP 的方法
  • Linux操作系统之线程(四):线程控制
  • HarmonyOS 启动提速秘籍:懒加载全链路实战解析
  • 反序列化漏洞4-Thinkphp5.4靶场安装及Thinkphp反序列化漏洞任意文件删除演示
  • 讲座|人形机器人多姿态站起控制HoST及宇树G1部署
  • python学智能算法(二十六)|SVM-拉格朗日函数构造
  • 什么是 ELK/Grafana
  • C#.NET EFCore.BulkExtensions 扩展详解
  • 手写tomcat
  • LINUX720 SWAP扩容;新增逻辑卷;逻辑卷扩容;数据库迁移;gdisk
  • DAY 20 奇异值分解(SVD)
  • RocketMQ核心编程模型
  • 咨询进阶——解读业务流程优化与重组【附全文阅读】
  • 5.2.4 指令执行过程
  • 【原创】微信小程序添加TDesign组件
  • ChatIM项目语音识别安装与使用
  • ARFoundation系列讲解 - 101 VisionPro 真机调试
  • USRP B210生成信号最大带宽测试之BPSK
  • 人脸识别:AI 如何精准 “认人”?
  • FreeSwitch编译部署
  • 【星海出品】python安装调试篇
  • 【数据集】NOAA 全球监测实验室(GML)海洋边界层(MBL)参考简介
  • Docker实践:使用Docker部署WhoDB开源轻量级数据库管理工具
  • 传输层协议 TCP
  • Java什么是原子性
  • Java SpringBoot 对接FreeSwitch
  • AtCoder Beginner Contest 415
  • Web-SQL注入数据库类型用户权限架构分层符号干扰利用过程发现思路
  • 向日葵远程命令执行漏洞