ToB企服应用市场:ToB评测及商务社交产业平台
标题:
探索HarmonyOS:一键掌握Router与NavPathStatck的传参和页面回调技巧
[打印本页]
作者:
三尺非寒
时间:
2024-12-21 02:49
标题:
探索HarmonyOS:一键掌握Router与NavPathStatck的传参和页面回调技巧
路由的选择
HarmonyOS
提供两种路由实现的方式,分别是
Router
和
NavPatchStack
。两者使用场景和殊效各有优劣。
组件适用场景特点备注Router模块间与模块内页面切换通过每个页面的url实现模块间解耦NavPathStack模块内页面切换通过组件级路由统一起由管理
什么时间使用
NavPatchStack
?
如果是单包应用开发,不使用动态包(hsp)举行拆包,只是使用静态包(har)简单的举行模块拆分,那么我保举使用 navPatchStack。
什么时间使用
Router
?
如果像开发 鸿蒙元服务,对单包体积有 2M 的限制,那么我们不得不使用动态包的方式。将相对独立的功能,二级页面等拆分出去,封装成动态包,可避开 dependencies 直接依赖得引用情势。
此时使用 router 跳转 url 的方式才可跳转到动态包内非直接引用的页面
NavPatchStatck 如何跳转(传参)及页面回调
NavPathStack
是配合
Navigation
一起使用的,
Navigation导航组件做统一的页面跳转管理,它提供了一系列属性方法来设置页面的标题栏、工具栏以及菜单栏的各种展示样式。
如何跳转(传参)及实现页面回调?
登录后复制
//第一步:定义一个用于传参和实现页面回调的模型
export interface RouterModel {
params?: Object, // 要传递给跳转页面的参数
popCallback?: (value: Object | undefined) => void // 用于页面回调
}
复制代码
1.
2.
3.
4.
5.
登录后复制
//第二步,需要在应用的根页面自行维护 navStack 实例,并传递给根节点 Navigation
@Provide('navPathStack') navPathStack: NavPathStack = new NavPathStack()
Navigation(this.pageInfos) {
Column() {}
}
.title('NavIndex')
.navDestination(this.PageMap)
// 统一管理维护路由跳转
@Builder
PageMap(name: string, params: RouterModel) {
if (name === 'pageOne') {
TestNavPathPage({ // TestNavPathPage 就是要跳转的目标页面
routerParams: params
})
} else {
// 默认空页面
}
}
复制代码
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
登录后复制
/// 任意一个页面获取 navPathStack 调用跳转并传参
@Component
export struct RouterCallbackExample {
@Consume('navPathStack') navPathStack: NavPathStack;
// NavPatchStack 方式跳转并获取回调
navPathStackJump() {
const routerParams: RouterModel = {
params: '我是入参 123', //传递到跳转页面的入参
popCallback: (callbackValue) => {
// 这里拿到回调结果,注意要判断 callbackValue !== undefine
// 这里拿到下面目标页面回传的结果 ‘我是回调的结果 345’
}
}
this.navPathStack.pushPathByName('pageOne', routerParams) // 'pageOne' 对应上面 'PageMap' 方法内定义的路径名称常量
}
build() {
Button('跳转').onClick(() => {
this.navPathStackJump()
})
}
}
复制代码
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
登录后复制
/// 目标页面接收入参、并返回页面回调
@Component
export struct TestNavPathPage {
@Consume('navPathStack') navPathStack: NavPathStack;
routerParams?: RouterModel
@State receiveParams: string = ''
aboutToAppear(): void {
// 接收入参,这里拿到上面传入的 ‘我是入参 123’
let receiveParams = this.routerParams!.params
}
build() {
NavDestination() {
Button('关闭页面并回调结果').onClick(() => {
if (this.routerParams?.popCallback !== undefined) {
this.routerParams.popCallback('我是回调的结果 345 ')
}
this.navPathStack.pop()
})
}.title('跳转目标页')
}
}
复制代码
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
Router 如何跳转(传参)及页面回调
Router
跳转可支持跳转本包内页面以及动态包大概拆包内的页面,
url 的定义举比方下:
登录后复制
1. 本地包内,或者直接依赖的静态包内页面,url 定义为 : pages/Page1
2. 分包内的页面,url 定义为 :@bundle:com.rex.harmony.atomic.service/featureName/ets/pages/Page2
// com.rex.harmony.atomic.service 是我的应用包名
// featureName 是跳转页面所在的模块名称,对应 module.json5 里面额 name
// ets/pages/Page2 为目标页面在模块内的页面路径,对应 main_pages.json 内的页面路径
复制代码
1.
2.
3.
4.
5.
6.
url 跳转
登录后复制
router.pushUrl({ url: '', params: Object })
复制代码
1.
留意: 使用 router + url 举行跳转的目的页面必须使用
@Entry
修饰,且在
main_pages.json
文件内填写对应路径
重点:截止 API11 版本,router 支持传递的 params 传参,不是引用传递,以是在动态包内实际获取到的不是同一个对象,为了实现页面回调,router 我们需要做如下封装:
如何跳转(传参)及实现页面回调?抛砖引玉 —
在公共的har包内定义 Router 管理类
FastRouter
(在下文扩展中解释单例为什么这么实现)
登录后复制
import { RouterModel } from './model/RouterModel'
import { router } from '@kit.ArkUI'
/// 基于 router 库封装,为了实现页面回调
export class FastRouter {
public readonly routerStack: RouterModel[] = []
/// 跨 hsp 使用这种方式实现单例
public static instance(): FastRouter {
const storageKey = 'REX_FAST_ROUTER'
if (!AppStorage.has(storageKey)) {
AppStorage.setOrCreate(storageKey, new FastRouter())
}
return AppStorage.get<FastRouter>(storageKey)!
}
/// 获取路由传递的入参
public static get getRouterCurrentParams(): RouterModel | undefined {
const stack = FastRouter.instance().routerStack
if (stack.length === 0) {
return undefined
}
return stack[stack.length - 1]
}
/// push 页面
public static async push(route: RouterModel): Promise<void> {
try {
await router.pushUrl({ url: route.url, params: route.params })
FastRouter.instance().routerStack.push(route)
} catch (_) {
console.log('>>>>')
}
}
/// replace 页面
public static async replace(route: RouterModel): Promise<void> {
try {
await router.replaceUrl({ url: route.url, params: route.params })
const instance = FastRouter.instance()
const list = instance.routerStack
if (list.length > 0) {
instance.routerStack.splice(instance.routerStack.length - 1, 1, route)
}
} catch (_) {
// 暂无处理
}
}
/// 退出栈顶页面
public static async pop(animated?: boolean): Promise<void> {
router.back()
const routerStack = FastRouter.instance().routerStack
routerStack.pop()
}
}
复制代码
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
任一页面使用 FastRouter 举行 url 跳转
登录后复制
// 跳转到 hsp 包(feature_hsp_page)内的 TestHspHomePage 页面
const routerParams: RouterModel = {
url: '@bundle:com.rex.harmony.atomic.service/feature_hsp_page/ets/pages/TestHspHomePage',
params: '我是入参 1488',
popCallback: (callbackValue) => {
if (callbackValue !== undefined) {
//这里获取跳转页的回调数据
//接收到下文中目标页面的回调结果:‘我是回调的结果 6100 ’
}
}
}
FastRouter.push(routerParams)
复制代码
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
在目的页面内汲取入参并回调结果
登录后复制
@Entry
@Component
struct Index {
routerParams?: RouterModel
aboutToAppear(): void {
this.routerParams = FastRouter.getRouterCurrentParams as RouterModel
let receiveParams = this.routerParams.params //这里接收入参,也就是上面传递的 ‘我是入参 1488’
}
build() {
Button('关闭页面并回调结果').onClick(() => {
if (this.routerParams?.popCallback !== undefined) {
this.routerParams.popCallback('我是回调的结果 6100 ')
}
FastRouter.pop()
})
}
}
复制代码
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
总结
NavPatchStack
和
Router
两种路由方式各有优劣,NavPatchStack 方便统一管理,Router 方便解耦,两者没有任何关联,可以一起使用,也可以单独使用。
扩展:动态包、静态包的使用差别
说到动态包(HAR)和静态包(HSP),这里扩展一下两者的区别。
静态包的 module.json5 文件,type 标识为 har
登录后复制
{
"module": {
"name": "静态包模块名称",
"type": "har",
"deviceTypes": [
"default",
"tablet"
]
}
}
复制代码
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
静态包的 module.json5 文件,type 标识为 shared
登录后复制
{
"module": {
"name": "动态包模块名称",
"type": "shared",
"description": "$string:shared_desc",
"deviceTypes": [
"phone",
"tablet"
],
"deliveryWithInstall": true,
"installationFree": true,
"pages": "$profile:main_pages"
}
}
复制代码
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
动态包和静态包都可以被直接引用,在 oh-package.json5 内
登录后复制
{
...省略
"dependencies": {
"@rex/任意名称": "file:../../base/静态包模块名称",
"@rex/任意名称": "file:../../base/动态包模块名称"
}
}
复制代码
1.
2.
3.
4.
5.
6.
7.
重点:
动态包可以不直接依赖( 比方:使用 preload ),跳转动态包内的页面可以通过 router 拼接 url 举行跳转
har 中的代码和资源跟随使用方编译,如果有多个使用方,存在多个 hap,也包罗使用了 hsp 的场景。该 har 的编译产物中会存在多份雷同拷贝。
存在两种情况,如果harA和harB都依赖harC,单个hap依赖harA、harB,那么只会存在一份harA、harB、harC;如果harA和harB都依赖harC,有两个hap,hapA依赖harA,hapB依赖harB,那么终极会存在一份harA、harB,两份harC;
hsp 中的代码和资源可以独立编译,运行时在一个进程中代码也只会存在一份
举个例子:
如果应用内没有使用动态包,大概把单例的封装放在动态包里被其他包(静态包或动态包)直接依赖,我们的单例可以这么写:
登录后复制
class SimgleProvider {
private static _instance?: SimgleProvider
public static instance(): SimgleProvider {
if (!SimgleProvider._instance) {
SimgleProvider._instance = new SimgleProvider()
}
return SimgleProvider._instance
}
}
复制代码
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
在应用的任意一处获取这个单例都是同一个对象
但如果单例封装放在 har 包内,该 har 包被其他包(静态包和动态包)依赖引用。仍然使用上述代码实现单例,我们在 entry 大概 被 entry 直接依赖的 har 中获取到的
SimgleProvider 单例
和在 hsp 包内获取到的
SimgleProvider 单例
不是指向同一份内存,它会被拷贝两份。
可以简单的总结为:一个应用内,同一个 har 包,如果同时被 har (大概entry)和 hsp 依赖引用,会被拷贝两份。
如果需要整个应用内,包罗 entry、har、hsp 都指向的是同一个单例对象,要么把这个单例封装放在 hsp 内,如果单例封装放在 har 内,需要把上面的单例实现代码改成如下:
登录后复制
class SimgleProvider {
private static _instance?: SimgleProvider
public static instance(): FastRouter {
const storageKey = 'SimgleProvider'
if (!AppStorage.has(storageKey)) {
AppStorage.setOrCreate(storageKey, new SimgleProvider())
}
return AppStorage.get<SimgleProvider>(storageKey)!
}
}
复制代码
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
借助
AppStorage
,实现全应用单例
上述示例demo已上传,请参考下方链接
附注(Example)
Demo 示例已上传:
GitHub: https://github.com/liyufengrex/HarmonyAtomicService
GitCode: https://gitcode.com/liyufengrex/HarmonyAtomicService
(基于API11开发,支持NEXT及以上版本运行)已上传可供参考,包罗如下内容:
静态库+动态包+多模块设计
状态管理
统一起由管理(router+navPathStack)
网络请求、Loading、Toast、数据长期化 等工具库封装
自定义组件、自定义弹窗(解耦)
EventBus 事件通知
扩展修饰器,实现 节省、防抖、权限申请
动态路由 (navPathStack + 动态import + WrappedBuilder)
UI动态节点操作 (BuilderNode + NodeController)
折叠屏适配示例
组件工厂示例
组件动态属性设置示例
登录后复制
// 工程目录
├──entry // ets代码区
│ └──src/main/ets
│ ├──entryability
│ │ ├──FoldStatusObserver.ets // 折叠屏幕变化监听
│ │ └──EntryAbility.ets
│ ├──pages
│ │ └──MainPage.ets // 首页
├──business // 放置静态包的文件夹(业务模块)
│ ├──feature_home // 放置首页Tab里的一些示例页面
│ │ └──/src/main/ets/pages
│ │ ├──HomePage.ets //首页的第一个Tab
│ │ ├──BuilderNodeExample.ets //动态节点操作示例
│ │ ├──CustomDialogExample.ets //自定义弹窗解耦
│ │ ├──EventBusExample.ets //消息通知
│ │ ├──HttpRequestExample.ets //网络请求示例
│ │ ├──PermissionExample.ets //使用注解请求权限
│ │ ├──RouterCallbackExample.ets //使用 NavPathStack 与 Route 两种方式实现页面跳转及回调(HSP、HAR)
│ │ ├──FixFoldUiExample.ets //折叠屏适配示例
│ │ ├──ComponentFactoryExample.ets //组件工厂示例
│ │ ├──AttributeModifierExample.ets //组件动态属性设置示例
│ │ └──ThrottleExample.ets //使用注解防抖
│ ├──feature_setting
│ │ └──/src/main/ets/pages
│ │ ├──SettingPages.ets //首页的第二个Tab
│ │ └──TestDynamicNavPage.ets //测试动态路由示例
├──features //放置动态包的文件夹
│ ├──feature_has_page
│ │ └──/src/main/ets/pages
│ │ ├──TestHspNavPathPage.ets //测试 NavPath 跳转 HSP 内页面
│ │ └──TestHspRouterPage.ets //测试 Route 跳转 HSP 内页面
├──base
│ ├──fast_ui //封装公共UI
│ │ ├──/src/main/ets/compnents
│ │ │ ├──FoldStatusContainer.ets // 折叠屏变化响应组件封装
│ │ │ ├──FastLoading.ets // loading工具
│ │ │ └──FastToast.ets // toast工具
│ │ └──/src/main/ets/styles // 公共样式
│ ├──fast_util // 通用工具
│ │ ├──/src/main/ets
│ │ ├──EventBus.ets // 消息通知+监听
│ │ ├──FastLog.ets // 日志打印
│ │ ├──FastNavRouter.ets // 用于动态路由
│ │ ├──FastPermission.ets // 请求权限注解器
│ │ ├──FastRouter.ets // 基于 router 库封装,为了实现页面回调
│ │ ├──FastTool.ets
│ │ ├──PreferencesUtil.ets // 数据持久化工具
│ │ └──ThrottleTool.ets // 防抖注解器
│ ├──global_constant
│ │
├──entry/src/main/resources // 应用资源目录
└──module.json5 // 添加卡片拓展能力
复制代码
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
补充
从
API version 12
开始,Navigation支持使用系统路由表的方式举行动态路由。各业务模块(HSP/HAR)中需要独立配置router_map.json文件(可参考上述demo内TestHspNavPathPage.ets文件)。
具体可参考文档: Navigation系统路由
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/)
Powered by Discuz! X3.4