第3篇:手把手教你如何实现对Navigation路由框架的封装!HarmonyOS 鸿蒙Next(鸿蒙0-1开发)
第3篇:手把手教你如何实现对Navigation路由框架的封装!HarmonyOS 鸿蒙Next(鸿蒙0-1开发)
<markdown _ngcontent-ffx-c237="" class="markdownPreContainer">
第3篇:手把手教你如何实现对Navigation路由框架的封装!
在上一篇中,我们已经知道了Navigation的基本使用,本篇我们将实现对Navigation进行封装,使得能适配更多项目。
一、从哪里开始入手?
首先,我们先看回去第2篇中跳转页面的逻辑:
[@Builder](/user/Builder)
pageMap(name: string, param: object) {
//TODO-这里最外层是NavDestination
//这两种效果是一样的
if (name === 'AboutMePage') {
AboutMePage()
} else if (name === 'AboutMe2Page') {
NavDestination() {
AboutMe2Page()
}
.mode(NavDestinationMode.STANDARD)
.hideTitleBar(true)
}
}
<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 4px; right: 8px; font-size: 14px;">复制</button>
我们来变一下:
[@Builder](/user/Builder)
function getAboutMeBuilder(_value: object): void {
AboutMePage();
}
[@Builder](/user/Builder)
function getAboutMe2Builder(_value: object): void {
AboutMe2Page();
}
@Builder
pageMap(name: string, param: object) {
//TODO-这里最外层是NavDestination
//这两种效果是一样的
if (name === ‘AboutMePage’) {
wrapBuilder(getAboutMeBuilder).builder(param)
} else if (name === ‘AboutMe2Page’) {
NavDestination() {
wrapBuilder(getAboutMe2Builder).builder(param)
}
.mode(NavDestinationMode.STANDARD)
.hideTitleBar(true)
}
}
<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 4px; right: 8px; font-size: 14px;">复制</button>
效果还不是很明显,那么我们再改变一下:
[@Builder](/user/Builder)
function getAboutMeBuilder(_value: object): void {
AboutMePage();
}
[@Builder](/user/Builder)
function getAboutMe2Builder(_value: object): void {
AboutMe2Page();
}
getBuilder(name: string): WrappedBuilder<[object]> {
if (name === ‘AboutMePage’) {
return wrapBuilder(getAboutMeBuilder)
} else /* if (name === ‘AboutMe2Page’)*/ {
return wrapBuilder(getAboutMe2Builder)
}
}
hasNavDest(name: string): boolean {
if (name === ‘AboutMePage’) {
return true;
} else /* if (name === ‘AboutMe2Page’)*/ {
return false;
}
}
@Builder
pageMap(name: string, param: object) {
if (this.hasNavDest(name)) {
this.getBuilder(name).builder(param)
} else {
NavDestination() {
this.getBuilder(name).builder(param)
}
.mode(NavDestinationMode.STANDARD)
.hideTitleBar(true)
}
}
<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 4px; right: 8px; font-size: 14px;">复制</button>
看!pageMap
函数里面已经可以固定了,我们只需要负责getBuilder
能够拿到相应的页面即可!而路由的封装原理,也就是基于这个思路来的!
二、动态import
现在大型的APP基本是多模块同步开发的,所以动态路由就变的至关重要了,而动态路由的重点就是动态import
,动态导入可以很好的节省了内存的使用。下面就来介绍如何使用。
- 2.我们以动态import本地HAR模块为案例,实现动态import。
- 3.我们以
登录模块
作为动态import的实施对象。
- 为了方便管理,我们现在根目录新建一个文件夹,命名为:
feature
,这这个目录下放置各模块,如:登录模块、分享模块等。 - 选中
feature
文件夹,右击->New->Module,选择Static Library
,取名为:login
。 - 我们把
login
模块添加依赖(oh-package.json5文件中)到entry
,其中[@module](/user/module)/login
自己随便取,但一定要与下面保持一致;file:相对路径
"dependencies": {
"[@module](/user/module)/login": "file:../../../feature/login",
}
<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 4px; right: 8px; font-size: 14px;">复制</button>
- 接着在
entry
的build-profile.json5
中添加包:
"buildOption": {
"arkOptions": {
// TODO:知识点:动态路由中使用了动态import变量表达式的能力,需要在这里配置模块名,和oh-package.json5中dependencies下面配置的模块名相同。
"runtimeOnly": {
"sources": [
],
"packages": [
"[@module](/user/module)/login",
]
}
}
},
<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 4px; right: 8px; font-size: 14px;">复制</button>
- 接着在
login
模块的根目录Index.ets
文件中添加一个函数:
export function harInit(pageName: string) {
//可以自己打印一下
}
<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 4px; right: 8px; font-size: 14px;">复制</button>
- 接下来,在entry的一个页面中,通过点击按钮来实现导入。
Button('动态导入登录模块')
.onClick(async () => {
await import("[@module](/user/module)/login").then((result: ESObject) => {
// 动态加载模块成功时,通过模块中的harInit接口加载页面
result.harInit("register"); //然后在harInit函数中进行,动态 registerPage 页面
}, (error: ESObject) => {
// 动态加载模块失败时,打印错误日志
});
})
<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 4px; right: 8px; font-size: 14px;">复制</button>
点击后,会调用5)
中的harInit
函数,而通过pageName
来决定导入哪一个界面,从而实现了动态import。
三、新建一个路由module
- 1.为了方便管理,我们现在根目录新建一个文件夹,命名为:
common
,这这个目录下放置常用的的工具类和路由管理类。 - 2.选中
common
文件夹,右击->New->Module,选择Static Library
,取名为:router
- 3.然后在所有需要用到路由跳转的module中添加依赖(oh-package.json5文件中),如:
entry
和login
:
"dependencies": {
"[@common](/user/common)/router": "file:../../../common/router",
}
<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 4px; right: 8px; font-size: 14px;">复制</button>
- 4.根据
pageMap
函数的情况,我们需要一个能表示一个页面的data
数据类,包含了判断name
和是否包含NavDestination
的内容,而根据模块动态加载的原因,需要把name
分成:moduleName
和pageName
,因此我们取一个类为:RouterInfo
,具体如下;
/*
* [@Desc](/user/Desc): 每个路由页面的组成部分,为动态import,所以需要先import(moduleName),然后再根据pageName来决定加载那个组件
* [@Author](/user/Author): qincji
* [@Date](/user/Date): 2024/6/19
*/
export interface RouterInfo {
moduleName: string //模块名称
pageName: string //页面名称
hasNavDest?: boolean //默认undefined, 而undefined和false表示该组件没有带 :NavDestination。
}
<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 4px; right: 8px; font-size: 14px;">复制</button>
- 5.我们再创建一个统一管理需要增加的页面的页面管理类,就取名为:
RouterPage
,详细代码如下:
/*
* [@Desc](/user/Desc): 路由页面统一管理页面,所有的页面都应该在这里添加,方便管理和使用!
* [@Author](/user/Author): qincji
* [@Date](/user/Date): 2024/6/20
*/
import { RouterInfo } from './RouterInfo';
export class RouterPage {
//方便管理
private static infos: Map<string, RouterInfo> = new Map<string, RouterInfo>();
//TODO - 需要注意的是 moduleName 一定需要与 entry中build-profile.json5、oh-package.json5中dependencies导入定义的一致
//moduleName和pageName并没有限制,唯一即可
static readonly LOGIN: RouterInfo = RouterPage.create("@module/login", “login”, true);
static readonly REGISTER: RouterInfo = RouterPage.create("@module/login", “register”);
static readonly ENTRY_MODULE_NAME: string = “@module/entry”;
static readonly ABOUT_ME: RouterInfo = RouterPage.create("@module/entry", “about_me”, true);
static readonly ABOUT_ME2: RouterInfo = RouterPage.create("@module/entry", “about_me2”);
private static create(moduleName: string, pageName: string, hasNavDest?: boolean): RouterInfo {
const path = moduleName + “/” + pageName;
let info = RouterPage.infos.get(path);
if (info) {
return info;
}
info = { moduleName: moduleName, pageName: pageName, hasNavDest: hasNavDest };
RouterPage.infos.set(path, info);
return info;
}
public static getInfo(path: string) {
return RouterPage.infos.get(path);
}
public static getPath(info: RouterInfo) {
return info.moduleName + “/” + info.pageName;
}
}
<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 4px; right: 8px; font-size: 14px;">复制</button>
很简单的代码,相信大家一看就能懂,就是
pageMap
中参数name
是由moduleName + "/" + pageName
组成的,而为了方便查找就增加了一个infos管理。
- 6.接下来是重点了,在一、从哪里开始入手?中最后改造的结果中,我们需要实现
getAboutMeBuilder
、getBuilder
和hasNavDest
这几个关键的函数。接下里我们一个一个详细来讲解。
- 首先我们先定义一个管理类,就叫
RouterNav
吧。 - 在这个类中,我们统一管理所有的类似
getAboutMeBuilder
的[@Builder](/user/Builder)
的注册,于是就有了:
import { RouterInfo } from './RouterInfo';
import { RouterPage } from './RouterPage';
export class RouterNav {
// 管理需要动态导入的模块,key是模块名,value是WrappedBuilder对象,动态调用创建页面的接口
private static routerMap: Map<string, WrappedBuilder<[object]>> = new Map<string, WrappedBuilder<[object]>>();
public static registerPage(info: RouterInfo, builder: WrappedBuilder<[object]>) {
RouterNav.routerMap.set(RouterPage.getPath(info), builder);
}
}
<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 4px; right: 8px; font-size: 14px;">复制</button>
- 那么我们需要怎么才能把页面进行
registerPage
呢?我们在login
模块中,添加一个页面叫做LoginPage
,然后当该类被import
时进行registerPage
,详细代码如下:
import { RouterNav, RouterPage } from '[@common](/user/common)/router';
//不需要[@Entry](/user/Entry)
[@Component](/user/Component)
export struct LoginPage {
build() {
NavDestination() {
Column() {
Text(‘这是登录页面, 有NavDestination’).fontSize(35)
}
.width(‘100%’)
.height(‘100%’)
}
.mode(NavDestinationMode.STANDARD)
.hideTitleBar(true)
}
}
@Builder
function getPage(_value: object): void {
LoginPage();
}
//在import(xxx/LoginPage)是会执行
RouterNav.registerPage(RouterPage.LOGIN, wrapBuilder(getPage))
<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 4px; right: 8px; font-size: 14px;">复制</button>
- 接着,我们要看在哪里进行
import(xxx/LoginPage)
呢?答案就是在login
模块的根目录Index.ets
文件中,具体如下:
import { RouterPage } from '[@common](/user/common)/router/Index';
// 动态import时需要调用的接口,接口名请使用harInit,用于动态加载页面
export function harInit(pageName: string) {
switch (pageName) {
case RouterPage.LOGIN.pageName:
import(’./src/main/ets/pages/LoginPage’);
//当import后,就会执行 RouterNav.registerPage(RouterPage.LOGIN, wrapBuilder(getPage)) ,从而添加到路由管理中
break;
}
}
<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 4px; right: 8px; font-size: 14px;">复制</button>
是不是很熟悉啊!因为我们上面已经见过了,那接下来看如何触发了。
- 我们接着在
RouterNav
改一下:增加push
函数,以及routerStack
队列。
import { log } from '[@common](/user/common)/utils';
import { RouterInfo } from './RouterInfo';
import { RouterPage } from './RouterPage';
const TAG = ‘RouterNav’;
export class RouterNav {
// 管理需要动态导入的模块,key是模块名,value是WrappedBuilder对象,动态调用创建页面的接口
private static routerMap: Map<string, WrappedBuilder<[object]>> = new Map<string, WrappedBuilder<[object]>>();
static navPathStack: NavPathStack = new NavPathStack();
// 通过数组实现自定义栈的管理
private static routerStack: Array<RouterInfo> = new Array();
public static async push(info: RouterInfo, parm: object | undefined = undefined,
navPath: NavPathInfo = new NavPathInfo(RouterPage.getPath(info), parm)) {
const moduleName: string = info.moduleName;
const pageName: string = info.pageName;
// 动态加载模块是否成功
let isImportSucceed: boolean = false;
// entry模块不需要动态加载源码
if (moduleName === RouterPage.ENTRY_MODULE_NAME) {
isImportSucceed = true;
} else {
// TODO:知识点:通过动态import的方式引入模块,在需要进入页面时才加载模块,可以减少主页面的初始化时间及占用的内存
await import(moduleName).then((result: ESObject) => {
// 动态加载模块成功时,通过模块中的harInit接口加载页面
result.harInit(pageName); //然后在harInit函数中进行,动态 registerPage 页面
isImportSucceed = true;
}, (error: ESObject) => {
// 动态加载模块失败时,打印错误日志
log.d(TAG, error)
});
}
if (isImportSucceed) {
RouterNav.routerStack.push(info);
if (info.hasNavDest) {
RouterNav.navPathStack.pushPath(navPath);
} else {
RouterNav.navPathStack.pushPath(navPath);
}
}
}
public static registerPage(info: RouterInfo, builder: WrappedBuilder<[object]>) {
RouterNav.routerMap.set(RouterPage.getPath(info), builder);
}
}
<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 4px; right: 8px; font-size: 14px;">复制</button>
其中最重点的就是
await import
部分代码,当外部调用push
时,import(moduleName)
会导入模块的Index.ets
文件,然后通过result.harInit
调用文件里面的函数!
- 接着把
navPathStack
在entry
的Navigation
替换一下变成了:Navigation(RouterNav.navPathStack)
。 - 回到主题,上面6个步骤我们只是
getAboutMeBuilder
对进行处理,接下来我们对getBuilder
和hasNavDest
也处理下,这两个很简单,在RouterNav
增加两个函数:
public static getBuilder(name: string): WrappedBuilder<[object]> {
const builder = RouterNav.routerMap.get(name);
const meth = builder as WrappedBuilder<[object]>
return meth;
}
public static hasNavDest(name: string): boolean {
const info = RouterPage.getInfo(name);
if (info === undefined) {
log.e(TAG, ${name} --> 没有注册路由!
)
return false;
}
if (info.hasNavDest === undefined) {
return false;
}
return info.hasNavDest;
}
<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 4px; right: 8px; font-size: 14px;">复制</button>
- 然后再回到
entry
变换一下pageMap
,如下:
[@Builder](/user/Builder)
pageMap(name: string, param: object) {
if (RouterNav.hasNavDest(name)) {
RouterNav.getBuilder(name).builder(param);
} else {
NavDestination() {
RouterNav.getBuilder(name).builder(param);
}
.mode(NavDestinationMode.STANDARD)
.hideTitleBar(true)
}
}
<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 4px; right: 8px; font-size: 14px;">复制</button>
- 最后,我们实现一下跳转,
Button('登录')
.onClick(() => {
RouterNav.push(RouterPage.LOGIN)
})
<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 4px; right: 8px; font-size: 14px;">复制</button>
- 我们把完整的
RouterNav
放出来:
/*
* [@Desc](/user/Desc): navigation路由的管理器,统一封装入栈和出栈等操作!
* [@Author](/user/Author): qincji
* [@Date](/user/Date): 2024/6/19
*/
import { log } from '[@common](/user/common)/utils';
import { RouterInfo } from './RouterInfo';
import { RouterPage } from './RouterPage';
const TAG = ‘RouterNav’;
export class RouterNav {
// 管理需要动态导入的模块,key是模块名,value是WrappedBuilder对象,动态调用创建页面的接口
private static routerMap: Map<string, WrappedBuilder<[object]>> = new Map<string, WrappedBuilder<[object]>>();
//方便查询
static navPathStack: NavPathStack = new NavPathStack();
// 通过数组实现自定义栈的管理
private static routerStack: Array<RouterInfo> = new Array();
/**
- 获取组件
- @param name
- @returns
*/
public static getBuilder(name: string): WrappedBuilder<[object]> {
const builder = RouterNav.routerMap.get(name);
const meth = builder as WrappedBuilder<[object]>
return meth;
}
/**
- 判断该组件是否包含:NavDestination
- @param name
- @returns
*/
public static hasNavDest(name: string): boolean {
const info = RouterPage.getInfo(name);
if (info === undefined) {
log.e(TAG,
${name} --> 没有注册路由!
)
return false;
}
if (info.hasNavDest === undefined) {
return false;
}
return info.hasNavDest;
}
/**
- 页面跳转
- @param info
- @param parm 需要传递给下一个页面的参数
- @param navPath
*/
public static async push(info: RouterInfo, parm: object | undefined = undefined,
navPath: NavPathInfo = new NavPathInfo(RouterPage.getPath(info), parm)) {
const moduleName: string = info.moduleName;
const pageName: string = info.pageName;
// 动态加载模块是否成功
let isImportSucceed: boolean = false;
// entry模块不需要动态加载源码
if (moduleName === RouterPage.ENTRY_MODULE_NAME) {
isImportSucceed = true;
} else {
// TODO:知识点:通过动态import的方式引入模块,在需要进入页面时才加载模块,可以减少主页面的初始化时间及占用的内存
await import(moduleName).then((result: ESObject) => {
// 动态加载模块成功时,通过模块中的harInit接口加载页面
result.harInit(pageName); //然后在harInit函数中进行,动态 registerPage 页面
isImportSucceed = true;
}, (error: ESObject) => {
// 动态加载模块失败时,打印错误日志
log.d(TAG, error)
});
}
if (isImportSucceed) {
RouterNav.routerStack.push(info);
if (info.hasNavDest) {
RouterNav.navPathStack.pushPath(navPath);
} else {
RouterNav.navPathStack.pushPath(navPath);
}
}
}
/**
- 需要带返回结果的方式跳转下个页面
- @param navPath
*/
public static async pushCallback(navPath: NavPathInfo) {
const info = RouterPage.getInfo(navPath.name);
if (info === undefined) {
return;
}
await RouterNav.push(info, undefined, navPath);
}
/**
- 在当前页面打开下一个页面
- @param info
- @param parm
- @param navPath
*/
public static async replace(info: RouterInfo, parm: object | undefined = undefined,
navPath: NavPathInfo = new NavPathInfo(RouterPage.getPath(info), parm)) {
const popInfo = RouterNav.routerStack.pop();
if (popInfo !== undefined) {
RouterNav.routerMap.delete(RouterPage.getPath(popInfo));
}
RouterNav.routerStack.push(info);
RouterNav.navPathStack.replacePath(navPath, true);
}
/**
- 返回
- @param result 返回的结果
*/
public static pop(result?: Object) {
if (result !== undefined) {
RouterNav.navPathStack.pop(result, true)
} else {
RouterNav.navPathStack.pop(true)
}
}
/**
<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 4px; right: 8px; font-size: 14px;">复制</button>
四、参数传递和注意事项
- 1.如果是跳转到
entry
页面,很好的通过import(moduleName)
进行动态加载,最简单的方式的直接预加载:
aboutToAppear(): void {
this.initImportEntryPage();
}
//TODO - 本entry的页面需要实现进行导入,很很费内存,但没有找到更好的动态导入的方案。所以不建议把非必要页面放在entry
private initImportEntryPage() {
new Promise<void>(async () => {
await import(’…/pages/AboutMePage’)
await import(’…/pages/AboutMe2Page’)
})
}
<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 4px; right: 8px; font-size: 14px;">复制</button>
- 2.如果
entry
有很多页面,上面的方式比较耗费内存,也可以改造成功动态导入,只是会麻烦了一点。
- 首先,我们在
entry
增加一个类,实现动态导入:
import { RouterInfo, RouterPage } from '[@common](/user/common)/router/Index';
export class PageLoader {
static async loadData(info: RouterInfo) {
if (info.pageName === RouterPage.ABOUT_ME.pageName) {
await import(’…/pages/AboutMePage’)
} else if (info.pageName === RouterPage.ABOUT_ME2.pageName) {
await import(’…/pages/AboutMe2Page’)
}
}
}
<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 4px; right: 8px; font-size: 14px;">复制</button>
- 我们在使用的过程中,先进进行动态导入:
Button('去关于我的页面')
.onClick(async () => {
await PageLoader.loadData(RouterPage.ABOUT_ME)
RouterNav.push(RouterPage.ABOUT_ME)
})
<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 4px; right: 8px; font-size: 14px;">复制</button>
- 3.上面两者两种各有优缺点,我的建议是看怎么使用:
- 如果是只有一个
entry
的项目的话,把router
也放在entry
,然后在push
里面实现await PageLoader.loadData
。 - 不建议在
entry
写非必要的页面。
- 4.如何页面之间如何实现传递参数呢?
- 把参数传递到下一页面,不需要回来结果:
Button('登录--》传些参数看看')
.onClick(() => {
const record: Record<string, Object> = {
'from': 'entry/home',
'text': this.value,
'age': 18,
}
RouterNav.push(RouterPage.LOGIN, record)
})
//然后在登录页面获取参数
@Component
export struct LoginPage {
@State result: string = “”;
build() {
NavDestination() {
Column() {
Text(‘这是登录页面, 有NavDestination’).fontSize(35)
Text(接收到的数据:${<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.result}
).fontSize(35)
}
.width(‘100%’)
.height(‘100%’)
}
.onReady(cxt => {
const record = cxt.pathInfo.param as Record<string, object>;
this.result = JSON.stringify(record);
})
.mode(NavDestinationMode.STANDARD)
.hideTitleBar(true)
}
}
<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 4px; right: 8px; font-size: 14px;">复制</button>
- 把参数传递到下一页面,且需要回来结果:
//entry
Button('登录--》传些参数看看')
.onClick(() => {
const record: Record<string, Object> = {
'from': 'entry/home',
'text': this.value,
'age': 18,
}
RouterNav.pushCallback({
name: RouterPage.getPath(RouterPage.LOGIN),
param: record,
onPop: async (info: PopInfo) => {
this.value = JSON.stringify(info);
//取得回来的参数
let params = (info.result as Record<string, Object>);
}
})
})
//login
@Component
export struct LoginPage {
@State result: string = “”;
build() {
NavDestination() {
Column() {
Text(‘这是登录页面, 有NavDestination’).fontSize(35)
Text(接收到的数据:${<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.result}
).fontSize(35)
Button(‘返回–》给点数据’)
.onClick(() => {
const record: Record<string, Object> = {
‘from’: ‘login/login’,
‘text’: “登录给你的”,
‘age’: 19,
}
RouterNav.pop(record)//返回结果,record可以是object
})
}
.width(‘100%’)
.height(‘100%’)
}
.onReady(cxt => {
const record = cxt.pathInfo.param as Record<string, object>;
this.result = JSON.stringify(record);
})
.mode(NavDestinationMode.STANDARD)
.hideTitleBar(true)
}
}
<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 4px; right: 8px; font-size: 14px;">复制</button>
到这里,所有的内容已经结束了!本章的完整源码已经上传到gitee了:鸿蒙应用0-1开发。
</markdown>能再上传一份完整代码例子供下载学习吗?
应该有的,是不是路径没对啊。试一下直接:git clone https://gitee.com/qincji/ZeroOneApp.git
本章: entry路径: ZeroOneApp/app/003/entry/src/main/ets/pages/EntryView.ets router路径: ZeroOneApp/common/router/src/main/ets/core/RouterNav.ets
ZeroOneApp/common/router/src/main/ets/core/RouterNav.ets 没有这个文件
升级的好快iiiiing
是的,官方一些demo应该也更新了
看了demo,确实不错,可以单独把路由模块拆分开来。就是使用同样是那么的复杂。
看得不是很明白!我就想问问,简单使用NavPathStack,Navigation,NavDestination
这三个,是不是要做一个[@Builder](/user/Builder)修饰的很多个if做成的路由表?比如一个app有几十个页面就要做几十个if判断?
dp2确实是需要这样做。
开发beta版本是API 12
在HarmonyOS鸿蒙Next中,实现对Navigation路由框架的封装,可以通过模块化设计,利用@Builder注解构建页面映射,并动态加载模块以节省内存。封装时需注意API版本差异,如API 10后推荐使用NavPathStack实现路由。封装应支持多显示模式,如单栏、分栏和自适应,并提供丰富的标题栏和工具栏配置。如果问题依旧没法解决,请加我微信,我的微信是itying888。