当前位置: 首页 > 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.dtcms.com/a/288996.html

相关文章:

  • 编程语言Java入门——核心技术篇(一)封装、继承和多态
  • 【图文详解】Transformer架构详细解析:多头自注意力机制、qkv计算过程、encoder架构、decoder架构以及mask的意义
  • Request和Response相关介绍
  • 假如只给物品编号和物品名称,怎么拆分为树形结构(拆出父级id和祖籍列表),用于存储具有层级关系的数据。
  • 高效培养AI代理的全能工具:Agent Reinforcement Trainer
  • Windows CMD(命令提示符)中最常用的命令汇总和实战示例
  • 【unitrix】 6.10 类型转换(from.rs)
  • 【windows 终端美化】Windows terminal + oh-my-posh 来美化命令行终端
  • Word for mac使用宏
  • 对粒子群算法的理解与实例详解
  • MybatisPlus-13.扩展功能-DB静态工具
  • Twisted study notes[2]
  • Linux——进程的退出、等待与替换
  • ThinkSound:阿里开源首个“会思考”的音频生成模型——从“看图配音”到“听懂画面”的技术跃迁
  • C++ Primer(第5版)- Chapter 7. Classes -004
  • Dockerfile配置基于 Python 的 Web 应用镜像
  • 考研最高效的准备工作是什么
  • docker制作前端镜像
  • JVM-Java
  • 每日算法刷题Day50:7.20:leetcode 栈8道题,用时2h30min
  • 全面解析 JDK 提供的 JVM 诊断与故障处理工具
  • 零基础学习性能测试第二章-JVM如何监控
  • Android系统5层架构
  • 【论文笔记】OccluGaussian解决大场景重建中的区域遮挡问题
  • 5G NR PDCCH之信道编码
  • c#:管理TCP服务端发送数据为非16进制
  • 4、ubuntu | dify创建知识库 | 上市公司个股研报知识库
  • Python知识点4-嵌套循环break和continue使用死循环
  • 统计与大数据分析和数字经济:专业选择指南
  • LP-MSPM0G3507学习--07定时器之二定时节拍