鸿蒙技术分享:Navigation页面容器封装-鸿蒙@fw/router框架源码解析(三)
本文是系列文章,其他文章见:
鸿蒙@fw/router框架源码解析(一)-router页面管理
鸿蒙@fw/router框架源码解析(二)-Navigation页面管理
鸿蒙@fw/router框架源码解析(四)-路由Hvigor插件实现原理
鸿蒙@fw/router框架源码解析(五)-无代码依赖如何实现拦截器逻辑
鸿蒙@fw/router框架源码解析(六)-模块化开发如何实现代码解耦
鸿蒙@fw/router框架源码解析
介绍
@fw/router是在HarmonyOS鸿蒙系统中开发应用所使用的开源模块化路由框架。
该路由框架基于模块化开发思想设计,支持页面路由和服务路由,支持自定义装饰器自动注册,与系统路由相比使用更便捷,功能更丰富。
具体功能介绍见https://juejin.cn/post/7386917612675301388@fw/router:鸿蒙模块化路由框架,助力开发者实现高效模块化开发!
基于模块化的开发需求,本框架支持以下功能:
- 支持页面路由和服务路由;
- 页面路由支持多种模式(router模式,Navigation模式,混合模式);
- router模式支持打开非命名路由页面;
- 页面打开支持多种方式(push/replace),参数传递;关闭页面,返回指定页面,获取返回值,跨页面获取返回值;
- 支持服务路由,可使用路由url调用公共方法,达到跨技术栈调用以及代码解耦的目的;
- 支持页面路由/服务路由通过装饰器自动注册;
- 支持动态导入(在打开路由时才import对应har包),支持自定义动态导入逻辑;
- 支持添加拦截器(打开路由,关闭路由,获取返回值);
- Navigation模式下支持自定义Dialog对话框;
详见gitee传送门
代码解析
FWNavigation
Navigation容器设置
因为本文章侧重于讲解@fw/router的实现逻辑,所以在上一节中并没有完整的讲Navigation页面如何使用。
其实,Navigation页面想要正常打开,除了注册页面外,还需要对导航容器进行设置。
具体如下:
@BuilderpageMap(name: string, param?: ESObject) {if (RouterManagerForNavigation.getInstance().getBuilder(name).builder.length == 0) {RouterManagerForNavigation.getInstance().getBuilder(name).builder()} else {RouterManagerForNavigation.getInstance().getBuilder(name).builder(param)}}build() {Navigation(this.pageStack) {// ...}.navDestination(this.pageMap)}
核心就是两点:
- Navigation容器需要绑定
NavPathStack
对象; - Navigation容器需要设置
navDestination
方法,因为它才是真正的页面跳转处理逻辑;
如果你的应用只使用Navigation进行页面管理,那么可能就只有一个Navigation容器,上面这些代码只需要设置一次,手动编写没什么问题。
但如果你准备router页面栈和Navigation页面栈混用,或者主用router页面栈但Dialog想用Navigation支持,那么理论上每个router页面都需要一个Navigation容器,上面的设置代码就需要写多次。
正是基于以上原因,@fw/router中封装了FWNavigation
容器。
FWNavigation整体代码
@Component
export struct FWNavigation {// 接受外部传入的AttributeModifier类实例@Prop modifier: NavigationModifier | null = null;@Provide('pageStack') pageStack: NavPathStack = new NavPathStack()aboutToAppear(): void {RouterManagerForNavigation.getInstance().pushNavPathStack(this.pageStack)}aboutToDisappear(): void {RouterManagerForNavigation.getInstance().popNavPathStack(this.pageStack)}@BuilderpageMap(name: string, param?: ESObject) {if (RouterManagerForNavigation.getInstance().getBuilder(name).builder.length == 0) {RouterManagerForNavigation.getInstance().getBuilder(name).builder()} else {RouterManagerForNavigation.getInstance().getBuilder(name).builder(param)}}@BuilderParam closure: Functionbuild() {Navigation(this.pageStack) {this.closure()}.navDestination(this.pageMap).titleMode(NavigationTitleMode.Mini).attributeModifier(this.modifier)}
}
FWNavigation的代码不多,但是大致也分为三部分,分别是容器设置,多容器逻辑,系统组件扩展。
FWNavigation容器设置
@Component
export struct FWNavigation {@BuilderpageMap(name: string, param?: ESObject) {if (RouterManagerForNavigation.getInstance().getBuilder(name).builder.length == 0) {RouterManagerForNavigation.getInstance().getBuilder(name).builder()} else {RouterManagerForNavigation.getInstance().getBuilder(name).builder(param)}}build() {Navigation(this.pageStack) {// ...}.navDestination(this.pageMap)}
}
容器设置代码就是我们在上一节中讲的,主要是绑定NavPathStack
对象和设置navDestination
方法。
多容器逻辑
@Component
export struct FWNavigation {@Provide('pageStack') pageStack: NavPathStack = new NavPathStack()aboutToAppear(): void {RouterManagerForNavigation.getInstance().pushNavPathStack(this.pageStack)}aboutToDisappear(): void {RouterManagerForNavigation.getInstance().popNavPathStack(this.pageStack)}build() {Navigation(this.pageStack) {}}
}
多容器逻辑主要是解决Navigation绑定的NavPathStack
对象从哪里来的问题。
如果整个应用只有一个Navigation容器,其实很简单,只需要让RouterManager单例自己创建NavPathStack
对象,Navigation使用即可。
但对于应用中存在多个Navigation容器的情况,就比较复杂了。
从以上的代码中,我们看到,NavPathStack
对象是由FWNavigation容器自己创建,并在aboutToAppear
方法中,讲之托管给了RouterManagerForNavigation单例。
在aboutToDisappear
方法中,也会将我们使用的NavPathStack
对象从RouterManagerForNavigation单例中移除。
export class RouterManagerForNavigation implements RouterHandler {// 多Navigation状态下,每个Navigation都要将自己的`navPathStacks`对象托管给管理器。navPathStacks: NavPathStack[] = [];get currentNavPathStack(): NavPathStack | undefined {return this.navPathStacks[this.navPathStacks.length-1]}pushNavPathStack(stack: NavPathStack) {this.navPathStacks.push(stack)}popNavPathStack(stack?: NavPathStack) {if (stack != undefined && this.navPathStacks.indexOf(stack) >= 0) {this.navPathStacks = this.navPathStacks.filter((item) => item !== stack)} else {this.navPathStacks.pop()}}
}
RouterManagerForNavigation主要是使用currentNavPathStack
方法,所以上面的处理主要是为了让使用的NavPathStack
对象和当前UI层展示的保持一致。
除了普通的push/pop场景,其实还有更复杂的情况,比如Tab嵌套。
当多个页面嵌入到Tab中时,我们建议Tab页外面统一套一层FWNavigation容器,Tab内页不套FWNavigation容器,否则当Tab页面selectedIndex变动时,还需要保证currentNavPathStack
获取到的对象和当前页面的NavPathStack
对象一致,否则Navigation页面无法正常显示。
系统组件扩展
FWNavigation容器其实也只是对系统Navigation容器进行了封装,为了更好的兼容性,理论上我们需要支持所有Navigation支持的属性。
好消息是,官方给我们提供了方案:AttributeModifier。
@Component
export struct FWNavigation {// 接受外部传入的AttributeModifier类实例@Prop modifier: NavigationModifier | null = null;@BuilderParam closure: Functionbuild() {Navigation(this.pageStack) {this.closure()}.attributeModifier(this.modifier)}
}
使用起来也算方便:
export struct TestPage {@State modifier: NavigationModifier = new NavigationModifier().mode(NavigationMode.Stack).subTitle('TestPage')build() {Column() {FWNavigation({ modifier: this.modifier }) {TestPageContent({ pageName: 'TestPage' })}}}
}
但坏消息是,即便是系统自己实现的NavigationModifier
,也并不是所有方法都可以使用。
有些属性你在IDE里可以调用,但运行会报错。
Error message:Method not implemented.
当你遇到这个报错时,很不幸,你要使用的属性并不支持。
其他方案
那么,除了AttibuteModifier
,还有其他方案吗?
肯定有,比如可以将Navigation所有支持的参数放到FWNavigation的构造方法入参中,自己对接实现。
但是该方案存在几个缺点:
- 代码逻辑不灵活,当系统api变动时自己也需要变动;
- 自定义组件不能使用链式语法,自定义参数只能放在构造方法入参中;也就是说如果现有代码从Navigation写法迁移到FWNavigation,无法通过改类名的方式直接迁移;(当然AttributeModifier也不行)
- 还有就是自己实现,无法使用系统api的默认取值。
第三个问题或许有点难以理解,下面详细解释下。
比如,Navigation有个属性叫hideToolBar
,是否隐藏工具栏。默认值:false。true: 隐藏工具栏。false: 显示工具栏。
我们看到系统的默认值现在是false。
我们在封装时,代码类似于:
class FWNavigationOptions {hideToolBar?: boolean
}struct FWNavigation {@Prop options: FWNavigationOptionsbuild() {Navigation() {}.hideToolBar(this.options.hideToolBar)}
}
现在的问题在于这一句.hideToolBar(this.options.hideToolBar)
。
我们自己封装的hideToolBar
是可选参数,可以为undefined。
但是系统的Navigation.hideToolBar()
入参却是必传参数,不能为undefined。
理论上有几种处理方法:
- 自己手动写死默认值:
.hideToolBar(this.options.hideToolBar ?? false)
;但面对Navigation20多种属性,写起来也太麻烦,而且那些方法类型的属性,还需要自己实现默认的方法,太复杂; - 通过条件渲染来避免
.hideToolBar()
方法调用;这种方法对于一两个属性的情况还行,属性多了就不行,毕竟它就是枚举,有10个属性你就要写2^10个条件分支语句;
所以最终,还是老老实实选择了AttributeModifier方案,虽然暂时还不完美,但还可以期待官方早点优化好……
总结
FWNavigation核心还是Navigation容器的封装扩展,对于@fw/router而言只是一个附加功能。
在混合栈的使用场景下,FWNavigation的价值比较明显,这也是@fw/router一开始进行封装的原因,对于鸿蒙开发而言,能够避坑的封装其实越早越好。
相关文章:
鸿蒙技术分享:Navigation页面容器封装-鸿蒙@fw/router框架源码解析(三)
本文是系列文章,其他文章见:鸿蒙fw/router框架源码解析(一)-router页面管理鸿蒙fw/router框架源码解析(二)-Navigation页面管理鸿蒙fw/router框架源码解析(四)-路由Hvigor插件实现原…...

三步入门Log4J 的使用
本篇基于Maven 的Project项目, 快速演示Log4j 的导入和演示。 第一步: 导入Log4j依赖 <dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-api</artifactId><version>2.24.2</version&…...

VBA中类的解读及应用第十八讲:利用类方法,判断任意单元格类型
《VBA中类的解读及应用》教程【10165646】是我推出的第五套教程,目前已经是第一版修订了。这套教程定位于最高级,是学完初级,中级后的教程。 类,是非常抽象的,更具研究的价值。随着我们学习、应用VBA的深入࿰…...

查询品牌涉及两张表(brand、brand_admin_mapping)
文章目录 1、BrandController2、AdminCommonService3、BrandApiService3、BrandCommonService4、BrandSqlService涉及的表SQL 查询逻辑参数处理执行查询完整 SQL 逻辑参数映射总结 查询指定管理员下的品牌所涉及的表有哪些? http://127.0.0.1:8087/brand/admin/list…...
Eureka和Zookeeper、Nacos的区别
目录 一、Eureka与Zookeeper的区别 适用场景: 架构设计: 功能特性: 社区生态: 二、Eureka与Nacos的区别 接口方式: 实例类型: 健康检测: 服务发现: 一致性与可用性&#…...

微信小程序怎么实现非tabbar页面显示tabbar,自定义组件实现
微信小程序没有发现可以实现非tabbar页面显示tabbar的方法,但是可以在tabbar页面当中隐藏tabbar,使用wx.hideTabBar()方法就可以实现,在非tabbar页面调用wx.showTabBar()方法却会显示失败,不能显示tabbar onLoad() {wx.showTabBar…...

SpringBoot如何使用EasyExcel实现表格导出(简洁快速入门版本)
前言 前面给大家介绍了动态表头的导入,这篇文章给大家介绍如何实现导出 前面给大家介绍了动态表头的导入,我们了解了如何通过EasyExcel灵活地读取结构不固定的Excel文件。这次,我们将目光转向数据导出——即如何将数据以Excel文件的形式输出…...
多种平台上安装部署调试Open5GS(四)
OpenWRT 源码安装 UERANSIM 安装依赖openwrt源码安装cmake其他依赖准备UERANSIM安装测试验证Open5GS 是一个功能完善的开源5G项目,具备5G、4G核心网功能,最新代码支持R17标准, 本系列文章介绍Open5GS在x86、ARM平台上的安装部署方法,并通过搭建UERANSIN、商用5G基站和终端两…...
单片机的基本构成与工作原理
单片机,即微控制器(Microcontroller Unit,MCU),是一种将中央处理器(CPU)、存储器(ROM/RAM)、定时/计数器(Timer/Counter)、中断系统、输入输出(I/O)接口等集成在一块芯片上的微型计算机。它具有体积小、功耗低、成本低…...
opencv常用图像处理操作
OpenCV 处理图像的通用流程通常包括以下几个步骤,根据具体需求可以调整或跳过某些步骤。以下是一个通用的框架: 读取图像 加载图像文件到内存中以进行后续处理。 import cv2 读取图像 image cv2.imread(‘image.jpg’) # 彩色图像 gray_image cv2…...

Svn如何切换删除账号
记录Svn清除切换账号 1.首先打开小乌龟的设置如下图 打开设置后单击已保存数据,然后选择清除 接上图选择清除后,就可以打勾选择清除已保存的账号,我们再次检出的就可以切换账号了 👉总结 本次记录Svn清除切换账号 如能帮助到你…...
使用PaddleOCR遇到的问题Bug
Q1: 训练模型的预测效果比inference模型的预测效果差距很大,怎么办?原因是什么? A1: 训练模型:使用训练数据集进行训练后的模型。 inference模型:把模型结构和模型参数保存在文件中的固化模型,多用于预测部署场景。训练过程中保存的模型是checkpoints模型,保存的只有模…...
了解Xcode在iOS开发中的作用和功能有哪些
Xcode是什么?它在iOS开发中的作用和功能有哪些? 一、Xcode是什么? Xcode是苹果公司针对macOS平台开发的一款集成开发环境(Integrated Development Environment,简称IDE)。它主要用于开发iOS、iPadOS、mac…...

《船舶物资与市场》是什么级别的期刊?是正规期刊吗?能评职称吗?
问题解答 问:《船舶物资与市场》是不是核心期刊? 答:不是,是知网收录的正规学术期刊。 问:《船舶物资与市场》级别? 答:国家级。主管单位:中国船舶集团有限公司 主办单…...

商汤完成组织架构调整,改革完成的商汤未来何在?
首先,从核心业务的角度来看,商汤科技通过新架构明确了以AI云、通用视觉模型等为核心业务的战略方向。这一举措有助于商汤科技集中资源,加强在核心业务领域的研发和市场拓展,提高市场竞争力。同时,坚定生成式AI为代表的…...

MyBatis异常体系中ErrorContext和ExceptionFactory原理分析
🎮 作者主页:点击 🎁 完整专栏和代码:点击 🏡 博客主页:点击 文章目录 exceptions包分包设计ExceptionFactory类介绍为什么使用工厂不是直接new呢?【统一的异常处理机制】【异常的封装与转化】【…...

WHLUG丨deepin、华中科技大学开放原子开源俱乐部、 RustSBI 和清华大学开源操作系统训练营共话开源新生代成长之路
2024年11月30日下午,由 deepin(深度)社区联合华中科技大学开放原子开源俱乐部、 RustSBI 开源社区和清华大学开源操作系统训练营共同举办的WHLUG(武汉Linux用户组)线下沙龙在华中科技大学成功举办。 本次活动聚集了50余…...

通过HTML Canvas 在图片上绘制文字
目录 前言 一、HTML Canvas 简介 二、准备工作 三、绘制图片 四、绘制文字 五、完整代码 效果演示: 前言 HTML canvas 为我们提供了无限的创意可能性。今天,我们就来探索一下如何通过 HTML canvas 将图片和文字绘制到图片上,创造出独特…...
C# 冒泡的算法
C# 冒泡的算法 public void BubbleSort(int[] arr) {int temp;for (int j 0; j < arr.Length - 2; j){for (int i 0; i < arr.Length - 2; i){if (arr[i] > arr[i 1]){temp arr[i 1];arr[i 1] arr[i];arr[i] temp;}}} }使用方法 int[] array new int[] { 5,…...

大数据项目-Django基于聚类算法实现的房屋售房数据分析及可视化系统
《[含文档PPT源码等]精品Django基于聚类算法实现的房屋售房数据分析及可视化系统》该项目含有源码、文档、PPT、配套开发软件、软件安装教程课程答疑等! 数据库管理工具:phpstudy/Navicat或者phpstudy/sqlyog 后台管理系统涉及技术: 后台使…...
synchronized 学习
学习源: https://www.bilibili.com/video/BV1aJ411V763?spm_id_from333.788.videopod.episodes&vd_source32e1c41a9370911ab06d12fbc36c4ebc 1.应用场景 不超卖,也要考虑性能问题(场景) 2.常见面试问题: sync出…...

【Oracle APEX开发小技巧12】
有如下需求: 有一个问题反馈页面,要实现在apex页面展示能直观看到反馈时间超过7天未处理的数据,方便管理员及时处理反馈。 我的方法:直接将逻辑写在SQL中,这样可以直接在页面展示 完整代码: SELECTSF.FE…...

微软PowerBI考试 PL300-选择 Power BI 模型框架【附练习数据】
微软PowerBI考试 PL300-选择 Power BI 模型框架 20 多年来,Microsoft 持续对企业商业智能 (BI) 进行大量投资。 Azure Analysis Services (AAS) 和 SQL Server Analysis Services (SSAS) 基于无数企业使用的成熟的 BI 数据建模技术。 同样的技术也是 Power BI 数据…...

边缘计算医疗风险自查APP开发方案
核心目标:在便携设备(智能手表/家用检测仪)部署轻量化疾病预测模型,实现低延迟、隐私安全的实时健康风险评估。 一、技术架构设计 #mermaid-svg-iuNaeeLK2YoFKfao {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg…...

【2025年】解决Burpsuite抓不到https包的问题
环境:windows11 burpsuite:2025.5 在抓取https网站时,burpsuite抓取不到https数据包,只显示: 解决该问题只需如下三个步骤: 1、浏览器中访问 http://burp 2、下载 CA certificate 证书 3、在设置--隐私与安全--…...

QT: `long long` 类型转换为 `QString` 2025.6.5
在 Qt 中,将 long long 类型转换为 QString 可以通过以下两种常用方法实现: 方法 1:使用 QString::number() 直接调用 QString 的静态方法 number(),将数值转换为字符串: long long value 1234567890123456789LL; …...

pikachu靶场通关笔记22-1 SQL注入05-1-insert注入(报错法)
目录 一、SQL注入 二、insert注入 三、报错型注入 四、updatexml函数 五、源码审计 六、insert渗透实战 1、渗透准备 2、获取数据库名database 3、获取表名table 4、获取列名column 5、获取字段 本系列为通过《pikachu靶场通关笔记》的SQL注入关卡(共10关࿰…...

CVE-2020-17519源码分析与漏洞复现(Flink 任意文件读取)
漏洞概览 漏洞名称:Apache Flink REST API 任意文件读取漏洞CVE编号:CVE-2020-17519CVSS评分:7.5影响版本:Apache Flink 1.11.0、1.11.1、1.11.2修复版本:≥ 1.11.3 或 ≥ 1.12.0漏洞类型:路径遍历&#x…...

【MATLAB代码】基于最大相关熵准则(MCC)的三维鲁棒卡尔曼滤波算法(MCC-KF),附源代码|订阅专栏后可直接查看
文章所述的代码实现了基于最大相关熵准则(MCC)的三维鲁棒卡尔曼滤波算法(MCC-KF),针对传感器观测数据中存在的脉冲型异常噪声问题,通过非线性加权机制提升滤波器的抗干扰能力。代码通过对比传统KF与MCC-KF在含异常值场景下的表现,验证了后者在状态估计鲁棒性方面的显著优…...
高防服务器价格高原因分析
高防服务器的价格较高,主要是由于其特殊的防御机制、硬件配置、运营维护等多方面的综合成本。以下从技术、资源和服务三个维度详细解析高防服务器昂贵的原因: 一、硬件与技术投入 大带宽需求 DDoS攻击通过占用大量带宽资源瘫痪目标服务器,因此…...