基础知识
目前官方推荐的最佳解决方案,是官方对于Navigation导航组件的封装,使用更简单便捷。如果熟悉Navigation的话,使用起来很快上手。
首先先集成HMRouter模块
使用命令行安装依赖:
ohpm install @hadss/hmrouter
或在模块的 oh-package.json5 文件中添加依赖
{ "dependencies": { "@hadss/hmrouter": "^1.0.0-rc.6", } }
插件配置
在项目级的 hvigor/hvigor-config.json5 中添加插件依赖
{ "dependencies": { "@hadss/hmrouter-plugin": "^1.0.0-rc.7" }, }
在模块的 hvigorfile.ts 文件中应用插件
import { hapTasks } from '@ohos/hvigor-ohos-plugin'; import { hapPlugin } from '@hadss/hmrouter-plugin'; //导入插件 export default { system: hapTasks, plugins:[hapPlugin()] //应用插件, } //还有harPlugin(),hspPlugin(),模块是什么类型,就用对应的方法
在项目的build-profile.json5中,配置useNormalizedOHMUrl属性为true
{ "app": { "products": [ { "name": "default", "signingConfig": "debug", "compatibleSdkVersion": "5.0.0(12)", "runtimeOS": "HarmonyOS", "buildOption": { "strictMode": { "useNormalizedOHMUrl": true } } } ], } }
HMRouter的配置就完成了。
初始化HMRouter
在入口的 UIAbility 类中(通常名字为EntryAbility )的 onCreate() 方法中调用HMRouter初始化方法,
export default class EntryAbility extends UIAbility { onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void { hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate'); //初始化HMRouterMgr HMRouterMgr.init({ context: this.context }) } }
定义首页
HMNavigation 用来包裹首页内容:
其中 homePageUrl 为加载时显示的页面地址,options中可以设置框架的属性(NavModifier)和首页的元素,设置内容基本与Navigation相同。
class NavModifier extends AttributeUpdater<NavigationAttribute> { initializeModifier(instance: NavigationAttribute): void { instance.mode(NavigationMode.Stack); instance.navBarWidth('100%'); instance.titleMode(NavigationTitleMode.Mini) instance.hideBackButton(true) // instance.hideNavBar(true) } } @Entry @Component struct HMRouterPage { static readonly TAG = 'HMRouterPage' modifier: NavModifier = new NavModifier() aboutToAppear(): void { HMRouterMgr.registerPageBuilder({ builder: wrapBuilder(PageOneBuilder), pageUrl: "pageOne", }) } build() { Column() { HMNavigation({ homePageUrl: '', navigationId: 'mainNavigation', options: { // standardAnimator: HMDefaultGlobalAnimator.STANDARD_ANIMATOR, // dialogAnimator: HMDefaultGlobalAnimator.DIALOG_ANIMATOR, modifier: this.modifier, title: { titleValue: "首页" }, menus: [{ value: "menu1", icon: "resources/base/media/menu1.png", action: () => { } }, { value: "menu2", icon: "resources/base/media/menu2.png", action: () => { } }] } }) { PageMain() } }.width('100%').height('100%') } }
定义子页
使用@HMRouter定义子页面路由
使用HMRouterMgr的 push(),replace(),pop()等方法进行页面之间的导航
使用HMRouterMgr.getCurrentParam()获取当页面的参数
//PageA @HMRouter({ pageUrl: 'pageA' }) @Component export struct PageA { build() { Column() { Text("Page A") Button("Page B").onClick(() => { HMRouterMgr.push({ pageUrl: 'pageB', param: "param" }) }) Button("Back").onClick(() => { HMRouterMgr.pop({ param: "pageA backParam" }) }) } } } //PageB @HMRouter({ pageUrl: 'pageB'}) @Component export struct PageB { @State params: string | undefined = HMRouterMgr.getCurrentParam()?.toString() build() { Column(){ Text("Page B") Text("params:" + this.params) Button("Page B").onClick(()=>{ HMRouterMgr.pop({ param: "pageB backParam" }) }) } } }
另一种方法HMRouterMgr.registerPageBuilder
使用HMRouterMgr.registerPageBuilder()动态注册子页面路由,子页面内容使用NavDestination组件包括,无缝将Navigation框架迁移到HMRouterMgr
//定义子页面builder @Builder export function PageOneBuilder() { PageOne() } @Component export struct PageOne { @State params: string | undefined = HMRouterMgr.getCurrentParam()?.toString() build() { NavDestination() { Column() { Text("page1") .padding(10) .fontSize(20) .fontWeight(500) } .width('100%') .height('100%') .alignItems(HorizontalAlign.Center) .justifyContent(FlexAlign.Center) } .title("page1") .menus([{ value: "menu1", icon: "resources/base/media/menu1.png", action: () => { } }, { value: "menu2", icon: "resources/base/media/menu2.png", action: () => { } }]) } } //动态注册路由 HMRouterMgr.registerPageBuilder({ builder: wrapBuilder(PageOneBuilder), pageUrl: "page1", })
HMRouter还有拦截器,生命周期,转场动画,服务路由等功能,限于篇幅不在细说,可以查看官方文档:
HMRouter官方文档
下面我们就开始搭建练习项目的路由框架
项目实践
注意:原本项目实战中打算使用HMRouter组件,实际发现使用HMRouter时菜单icon无法动态更新,因此替换成Navigation组件。
我们的demo中涉及三个页面,首页,详情页,作者页。首页作为路由主页面,详情页及作者页作为路由子页面。因此我们只需要注册后2个页面路由即可。
首页
@Builder function PageMap(name: string) { if (name == PageURls.PoetryDetailPage) { PoetryDetailPage() } else if (name == PageURls.AuthorPage) { AuthorPage() } } //首页 @Entry @Component struct Index { @Provide('navPathStack') navPathStack: NavPathStack = new NavPathStack() build() { Stack() { Navigation(this.navPathStack) { Column() { Button(PageURls.PoetryDetailPage) .onClick(() => { this.navPathStack.pushPath({ name: PageURls.PoetryDetailPage }) }) } } .navDestination(PageMap) .titleMode(NavigationTitleMode.Mini) .title("首页") .hideBackButton(true) }.width('100%').height('100%') } }
详情页
//详情页 @Component export struct PoetryDetailPage { @Consume('navPathStack') navPathStack: NavPathStack build() { NavDestination() { Text("详情页") }.title({ main: this.poetry!!.title, sub: `[${this.poetry!!.dynasty}]${this.poetry!!.author}` }).menus([{ value: "作者", icon: "resources/base/media/ic_person_24px.svg", action: () => { this.navPathStack.pushPath({ name: PageURls.AuthorPage }) } }]) } }
作者页
//作者页 @Component export struct AuthorPage { @Consume('navPathStack') navPathStack: NavPathStack build() { NavDestination() { Column() { Text("AuthorPage") } .alignItems(HorizontalAlign.Center) .justifyContent(FlexAlign.Center) .width('100%') .height('100%') }.title(this.name) } }
//PageUrl export class PageUrls{ public static PoetryDetailPage = 'PoetryDetailPage' public static AuthorPage = 'AuthorPage' }
到此我们基本的导航框架已经搭建完成,下一步我们来实现首页的列表展示。
本文的技术设计和实现都是基于作者工作中的经验总结,如有错误,请留言指正,谢谢。