鸿蒙开发:Navigation路由组件使用由繁入简

程序员一鸣
• 阅读 1

前言

本文基于Api12

之前的文章,针对Navigation做了简单的分析,无论是静态配置还是动态配置,都有着手动配置的需要,本篇文章就是要解决手动配置,利用路由库和插件实现自动配置的需求。

本篇文章大纲如下:

1、主页面和子页面模版(了解)

2、封装统一路由静态库(了解)

3、分析哪些需要插件自动完成(了解)

4、路由库和插件依赖

5、结合标题栏组件简化模版

6、路由库功能调用

7、简单总结

一、主页面和子页面模版(了解)

Navigation是路由容器组件,一般作为首页的根容器,也就是说,我们只在首页面使用即可,对于所有的子页面使用NavDestination即可,项目中的页面,无外乎首页面和子页面,首页面还好,书写一次即可,而子页面就非常非常的多了,所以,需要针对模版进行抽取。

以下简单贴出两个模版,此模版必须在有统一路由库的基础之上进行使用。

主页面模版:

@Entry
@Component
struct Index {
 private pageStack: NavPathStack = new NavPathStack()

  aboutToAppear() {
    RouterModule.createRouter(RouterNameConstants.ENTRY_HAP, this.pageStack);
  }

  @Builder
  routerMap(builderName: string, param: object) {
    RouterModule.getBuilder(builderName).builder(param);
  }

  build() {
    Navigation(this.pageStack) {
      RelativeContainer() {
       //其他UI
      }
      .width('100%')
      .height('100%')
    }.width('100%')
    .height('100%')
    .hideTitleBar(true)
    .navDestination(this.routerMap);

  }
}

子页面模版:

@Component
struct TestPage {
  build() {
    Column() {
      Text("子页面")
    }
    .width('100%')
    .height('100%')
  }
}

@Builder
export function TestBuilder(value: object) {
  NavDestination() {
    TestPage()
  }
  .hideTitleBar(true)
}

const builderName = BuilderNameConstants.Test;
if (!RouterModule.getBuilder(builderName)) {
  const builder: WrappedBuilder<[object]> = wrapBuilder(TestBuilder);
  RouterModule.registerBuilder(builderName, builder);
}

以上两个模版,特别是子页面,我们不可能每创建一个页面都要书写这么多冗余代码,显然是不合理的,在路由库和插件依赖一项中,会针对其进行抽取。

二、封装统一路由静态库(了解)

要实现各个动态组件的交互,比如主模块和动态共享包,动态共享包和动态共享包,动态共享包和静态共享包等等之间进行跳转,必须需要一个统一的路由组件库进行桥梁作用,这个库,可以直接放到业务层,主要的作用就是路由的相关功能配置,具体的相关工具,可以看之前的文章。

三、分析哪些需要插件自动完成

在正常的开发中,针对页面,我们正常书写即可,无须其他的额外配置,路由的操作应该和移动端的路由操作一致,只需要一个简单的注解就可以,那么基于此逻辑,其一、子页面中注册配置,NavDestination配置等等都是可以抽取的。

正常的子页面应该清洁无瑕,就是简单的UI组件,其他的都统统交给插件自己生成,自己实现配置。

@RouterPath("entry_test")
@Component
export struct TestPage {
  build() {
    //UI组件
  }
}

其二,就是每个子页面的动态导入import,在上一篇文章中,是需要手动的在初始化中进行配置,针对这个繁琐的程序,也应该交由插件进行配置。

四、路由库和插件依赖

以上三点只是做一个简单的了解,以哪些模版作为生成,以哪些代码需要抽取,这些步骤都已经做了封装,我们简单看下,封装后的路由功能实现。

第一步,配置依赖项

方式一:在需要Module中的oh-package.json5中设置三方包依赖,配置示例如下:

"dependencies": { "@abner/router": "^1.0.2"}

方式二:在Terminal窗口中,执行如下命令安装三方包,DevEco Studio会自动在工程的oh-package.json5中自动添加三方包依赖。建议:在使用的模块路径下进行执行命令。

ohpm install @abner/router

第二步,配置插件

配置插件前,请务必保障你的所有用到路由的Module都依赖了@abner/router,当然你也可以采用中间件的方式进行依赖。

依赖插件

找到项目中的hvigor目录,在hvigor-config.json5文件中dependencies配置插件。

代码如下:当前版本为:1.0.8

"dependencies": {
  "ohos-router": "1.0.8"
}

执行插件方法,打开根目录中的hvigorfile.ts文件,在plugins数组中导入方法:

plugins:[
  abnerRouter()
]

导包:

import { abnerRouter } from 'ohos-router/router-plugin';

插件完成之后,编译项目,你会发现,每个Module中,都会生成一个路由配置文件,以Module名字+RouterConfig为文件命名。

此路由配置文件为自动生成,无特殊情况下无须改动,当然,再有特殊情况下,你可以进行手动更正。

主页面模版

@Entry
@Component
struct Index {
  private navPathStack: NavPathStack = new NavPathStack()

  aboutToAppear() {
    routerInitNavPathStack(this.navPathStack)
  }

  @Builder
  routerMap(builderName: string, param: Object) {
    routerReturnBuilder(builderName).builder(param)
  }

  build() {
    Navigation(this.navPathStack) {
      RelativeContainer() {
        //其他组件
      }
      .height('100%')
      .width('100%')
    }.navDestination(this.routerMap)
  }
}

子页面模版

子页面没有模版,正常书写UI即可,需要配置一个注解@RouterPath设置一个别名即可,此别名需要模块名字+别名。

@RouterPath("entry_test")
@Component
export struct TestPage {
  build() {
    //UI组件
  }
}

五、路由库功能调用

1、普通跳转

startPage支持自己Module下跳转,同样也支持跨组件模式跳转。

startPage("entry_main") //entry_main和页面注解要一一对应

2、带数据跳转

支持常见的数据传递,比如对象、字符串、数组等等

字符串

startPage("shared_show_params", { param: "一个字符串" })

number

startPage("shared_show_params", { param: 100 })

boolean

startPage("shared_show_params", { param: true })

数组

startPage("shared_show_params", { param: [1, 2, 3, 4, 5, 6] })

对象

startPage("shared_show_params", { param: new SharedBean() })

直接传递对象

startPage("shared_show_params", { param: { "name": "AbnerMing", "age": 20 } })

传递Map

  let map = new HashMap<string, Object>()
map.set("name", "AbnerMing")
map.set("age", 18)
startPage("shared_show_params", { param: map })

3、接收参数

通过getRouterParams()方法获取上一个页面传递的数据。

 aboutToAppear(): void {
  let params = routerGetParams()
  // let map = params as number//number
  // let map = params?.toString() //string
  // let map = params as boolean //boolean
  // let map = params as string[] //数组
  // let map = params as SharedBean //对象
  // let map = params as HashMap<string, Object>//如果是传递的map,自己遍历即可

}

4、返回上一页面

finishPage()

5、返回指定页面

传递页面路由即可。

finishPageToName("entry_back_01")

6、直接返回首页

routerClearPage()

7、返回带数据

跳转时,使用result进行监听返回的参数

startPage("static_return_params", {
  result: (params) => {
    //获取返回的数据
    console.log("==========" + params?.toString())
  }
})

销毁页面时携带返回数据即可。

finishPage({ param: "我是返回的数据" })

8、关于生命周期

当子组件使用了NavDestination之后,会和组件的生命周期发生冲突,为解决这个问题,大家可以使用NavDestination的生命周期进行解决。

目前提供了setRouterLifeCycle方法用于监听其生命周期,可以在子页面中进行实现。

案例如下:

aboutToAppear(): void {
  setRouterLifeCycle({
    onShown: () => {
      console.log("==========显示")
    },
    onHidden: () => {
      console.log("==========隐藏")
    }
  })
}

相关方法一览:

方法 概述
onWillAppear NavDestination创建后,挂载到组件树之前执行,在该方法中更改状态变量会在当前帧显示生效
onAppear 通用生命周期事件,NavDestination组件挂载到组件树时执行。
onWillShow NavDestination组件布局显示之前执行,此时页面不可见(应用切换到前台不会触发)。
onShown NavDestination组件布局显示之后执行,此时页面已完成布局。
onWillHide NavDestination组件触发隐藏之前执行(应用切换到后台不会触发)。
onHidden NavDestination组件触发隐藏后执行(非栈顶页面push进栈,栈顶页面pop出栈或应用切换到后台)
onWillDisappear NavDestination组件即将销毁之前执行,如果有转场动画,会在动画前触发(栈顶页面pop出栈)
onDisappear 通用生命周期事件,NavDestination组件从组件树上卸载销毁时执行。

三、常见方法汇总

方法 参数 概述
startPage builderName: string, model?: RouterModel 跳转页面
replacePage builderName: string, model?: RouterModel 替换页面栈操作
startPageByName name: string, param: Object, onPop: Callback, animated?: boolean 将name指定的NavDestination页面信息入栈,传递的数据为param,添加onPop回调接收入栈页面出栈时的返回结果,并进行处理。
startPageDestination info: NavPathInfo, options?: NavigationOptions 将info指定的NavDestination页面信息入栈,使用Promise异步回调返回接口调用结果,具体根据options中指定不同的LaunchMode,有不同的行为。
startPageDestinationByName name: string, param: Object, onPop: Callback,animated?: boolean 将name指定的NavDestination页面信息入栈,传递的数据为param,并且添加用于页面出栈时处理返回结果的OnPop回调,使用Promise异步回调返回接口调用结果。
finishPage model?: PopModel 销毁页面
finishPageToName name: string, model?: PopModel 回退路由栈到由栈底开始第一个名为name的NavDestination页面。
routerSetInterception interception: NavigationInterception 设置Navigation页面跳转拦截回调。
routerClearPage animated?: boolean 清除栈中所有页面
routerGetParentNavPathStack 无参 清除栈中所有页面
routerDisableAnimation value: boolean 关闭(true)或打开(false)当前Navigation中所有转场动画。
routerMoveIndexToTop index: number, animated?: boolean 将index指定的NavDestination页面移到栈顶。
routerMoveToTop name: string, animated?: boolean 将index指定的NavDestination页面移到栈顶
routerPopToIndex index: number, result: Object, animated?: boolean 回退路由栈到index指定的NavDestination页面,并触发onPop回调传入页面处理结果。
routerRemoveByName name: string 将页面栈内指定name的NavDestination页面删除。
routerRemoveByIndexes indexes: Array<number> 将页面栈内索引值在indexes中的NavDestination页面删除。
routerReplacePathByName name: string, param: Object, animated?: boolean 将当前页面栈栈顶退出,将name指定的页面入栈
routerRemoveByNavDestinationId navDestinationId: string 将页面栈内指定navDestinationId的NavDestination页面删除。navDestinationId可以在NavDestination的onReady回调中获取,也可以在NavDestinationInfo中获取。
routerInitBuilder builderName: string, builder: WrappedBuilder<[Object]> 初始化Builder
routerInitConfig routerConfig?: RouterConfig[] 初始化配置
routerGetParams 无参 获取传递的参数

六、结合标题栏组件简化模版

如果你想结合我的另一个开源库bar进行使用,那么需要更换插件,由ohos-router切换到abner-router,其他都不变

"abner-router": "1.0.0"

依赖之后的模版,就非常的简单了,对于首页面,只需要使用MainView包裹即可。

首页模版

@Entry
@Component
struct Index {
  build() {
    RelativeContainer() {
      MainView() {
        //其他组件
      }
    }
    .height('100%')
    .width('100%')
  }
}

子页面模版

@RouterPath("entry_test")
@Component
struct TestPage {
  build() {
    TitleLayout({
      title:"我是标题"
    }) {
      //任意组件
    }
  }
}

七、简单总结

使用了插件和路由库之后,在每个Module下都会生成一个路由配置文件,以Module名字+RouterConfig为文件命名,此路由配置文件,也会在AbilityStage中,通过routerInitConfig方法进行自动配置。

子页面注解的作用很简单,用来标记页面的唯一标识,也就是别名,要求格式为:Module名字+下划线+定义的别名。

如entry Module下:entry_test1,entry_test2等等

如test Module下:test_test1,test_test2等等

如果你想对Module下的路由进行统一管理,便于查找路由和修改路由,目前进支持在Module根目录下进行配置。

比如entry目录下,你可以创建任意的管理类EntryRouter:

export class EntryRouter {
  static EntryTest= "entry_test"//测试页面
}

使用不变,直接获取即可。

@RouterPath(EntryRouter.EntryTest)
@Component
export struct TestPage {
  build() {
    //UI组件
  }
}
点赞
收藏
评论区
推荐文章
Alex799 Alex799
4年前
Web APP UI一致性设计
前言做前端开发一段时间后,你会不会发现自己在持续的做着页面重复开发的工作,后面甚至干脆是CtrlC、CtrlV操作。你可能会说,那就使用组件啊!的确,通过抽取公用视图,创建子组件的方式确实可以提升代码复用度。上面是回答是基于你
Easter79 Easter79
3年前
taro 组件的外部样式和全局样式
自定义组件对应的样式文件,只对该组件内的节点生效。编写组件样式时,需要注意以下几点:1.组件和引用组件的页面不能使用id选择器(a)、属性选择器(\a\)和标签名选择器,请改用class选择器。2.组件和引用组件的页面中使用后代选择器(.a.b)在一些极端情况下会有非预期的表现,如遇,请避免使用。3.子
Stella981 Stella981
3年前
Android与H5混合开发
    Android和H5在移动开发应用中非常广泛。市面上很多App都是使用Android开发的,但使用Android来开发一些比较复杂附属类,提示性的页面是得不偿失的。而H5在制作炫酷动画网页方面比较给力,且具有开发速度快,更新不用依赖于App的更新,只需要服务端更新相应的页面即可,所以App和H5页面相结合就显得尤为重要。而android
Wesley13 Wesley13
3年前
3. Vue
路由是根据不同的url地址展现不同的内容或页面。前端路由就是把不同路由对应不同的内容或页面的任务交给前端来做(在单页面应用,大部分页面结构不变,只改变部分内容的使用),之前是通过服务器根据url的不同返回不同的页面。前端路由优点:用户体验好,不需要每次都从服务器全部获取,快速展现给用户缺点:不利于SEO,使用浏
Stella981 Stella981
3年前
React Native 开发豆瓣评分(七)首页组件开发
首页内容拆分看效果图,首页由热门影院、豆瓣热门、热门影视等列表组成,每个列表又由头加横向滑动的电影海报列表构成。所以可以先把页面的电影海报、评分、列表头做成组件,然后在使用ScrollView将内容包裹即可构成首页。<divaligncenter<imgsrc"https://img2018.cnblogs.co
Wesley13 Wesley13
3年前
10分钟彻底搞懂单页面应用路由
上一次,跟大家科普了小程序的自定义路由routes,开启了路由之旅;今天,顺势就单页面应用路由,跟大家唠个五毛钱,如果唠得不好……退…一块钱?单页面应用特征假设:在一个web页面中,有1个按钮,点击可跳转到站内其他页面。多页面应用:点击按钮,会从新加载一个html资源,刷新整个页面;单页面应
融云IM即时通讯 融云IM即时通讯
6个月前
融云IM干货丨有没有插件能帮我优化uni-app的页面加载速度?
根据您的需求,以下是一些可以帮助优化uniapp页面加载速度的插件和方法:1.图片懒加载插件:使用图片懒加载可以显著减少首屏的加载时间。可以在页面滚动时才加载图片,减少初次加载的压力。2.代码拆分和懒加载:根据页面和功能的使用情况,将代码拆分为多个模块,并
程序员一鸣 程序员一鸣
4星期前
鸿蒙开发:一文探究Navigation路由组件
如果你还在使用router做为页面跳转,建议切换Navigation组件作为应用路由框架,不为别的,因为官方目前针对router已不在推荐。
程序员一鸣 程序员一鸣
2星期前
鸿蒙开发:了解@Builder装饰器
@Builder装饰是鸿蒙UI开发中,非常重要的一个装饰器,在实际的开发中,合理且正确的使用,能够让我们的代码更加的简洁,有两点需要注意,一是,是用私有还是全局,取决于当前的组件的复用机制,如果多个页面都使用了,建议以全局为主;二是传参的动态更新,有更新就使用引用参数传递,没有更新按值传递即可。
程序员一鸣 程序员一鸣
12小时前
HarmonyOs开发:轮播图Banner组件封装与使用
轮播图在每个项目中都很常见,鸿蒙中在容器组件中也提供了Swiper组件,用于子组件滑动轮播显示,和前端的使用起来也是异曲同工,我们先看下基本的用法。