鸿蒙开发之路由控制(API12)

334 阅读6分钟

这篇文档主要介绍了两种路由控制方式:router方式和Navigation方式,并详细阐述了它们的使用场景、优缺点和具体使用方法

1. 路由控制两种方式

  • router方式-更适用 用于模块间与模块内页面切换,通过每个页面的url实现模块间解耦
  • Naviagtion- 模块内页面跳转时,为了实现更好的转场动效场景不建议使用router该模块,推荐使用Navigation

2. Navigtion的使用

2.1. 介绍

  • Navigation组件是路由导航的根视图容器,一般作为Page页面的根容器使用,其内部默认包含了标题栏、内容区和工具栏,其中内容区默认首页显示导航内容(Navigation的子组件)或非首页显示(NavDestination的子组件),首页和非首页通过路由进行切换。
  • 优点:一次开发,多端部署场景下,使用Navigation在不同设备中会得到不同的视觉体验
  • 缺点:使用门槛较高 , Navigation的这种跳转方式耦合度较高,不适合大型的项目解耦开发。后续可以作为入口页的应用

2.2. 使用方法

● 最外层Navigation
● 有一个控制跳转的对象NavPathStack(需要new)
● 绑定到Navgation上面
● 需要传导到下方的每一层组件,使用这个对象进行跳转和替换
● 子组件(页面)放置到NavDestintion中
● Navigation组件需要配置.navDestion属性(builder)
import { promptAction, PromptAction } from '@kit.ArkUI'

@Entry
@Component
struct NavigationCase2 {
  @Provide
  stackPath: NavPathStack = new NavPathStack() // 声明一个pathStack对象

  @Styles
  gridStyle () {
    .height(100)
    .borderRadius(10)
    .backgroundColor(Color.Red)
    .margin(10)

  }
  @Builder
  getPageContent (name: string) {

      if(name === "friend") {
        // 渲染朋友圈组件
        Friend()
      }
      else if(name === "my") {
        // 渲染朋友圈组件
        My()
      }
      else if(name === "connect") {
        // 渲染朋友圈组件
        Connect()
      }
      else if(name === "chat") {
        // 渲染朋友圈组件
        Chat()
      }

  }
  build() {
    // 绑定关系
    Navigation(this.stackPath) {
    //  四个导航 导航不同的页面
      // 朋友圈 我的 联系人 聊天
      GridRow ({ columns: 2 }) {
        GridCol() {
          Text("朋友圈")
            .fontColor(Color.White)
        }
        .gridStyle()
        .onClick(() => {
          // this.stackPath.pushPath({
          //   name: 'friend'
          // })
         this.stackPath.pushPathByName("friend", null)
        })
        GridCol() {
          Text("我的")
            .fontColor(Color.White)
        }  .gridStyle()
        .onClick(() => {
          this.stackPath.pushPathByName("my", null)
        })
        GridCol() {
          Text("联系人")
            .fontColor(Color.White)
        }  .gridStyle()
        .onClick(() => {
          this.stackPath.pushPathByName("connect", null)
        })
        GridCol() {
          Text("聊天")
            .fontColor(Color.White)
        }  .gridStyle()
        .onClick(() => {
          this.stackPath.pushPathByName("chat", null)
        })
      }
    }
    .title("微信主页")
    .titleMode(NavigationTitleMode.Mini)
    .navDestination(this.getPageContent)
  }
}

@Component
struct Friend {
  @Consume
  stackPath: NavPathStack
  build() {
    NavDestination() {
      Text("朋友圈组件")
      Button("到我的").onClick((event: ClickEvent) => {
         // this.stackPath.pushPathByName("my", null)
        this.stackPath.replacePathByName("my", null)
      })
    }
    .title("朋友圈")
  }
}
@Component
struct My {
  build() {
    NavDestination() {
      Text("我的")
    }
    .title("我的")
  }
}
@Component
struct Connect {
  build() {
    NavDestination() {
      Text("联系人")
    }
    .title("联系人")
  }
}
@Component
struct Chat {
  build() {
    NavDestination() {
      Text("聊天")
    }
    .title("聊天")
  }
}

import { RouterBuilder } from './builders'

@Entry
@Component
struct NavigationPathCase {
  @Provide
  stackPath: NavPathStack = new NavPathStack() // 实例化一个对象绑定到Navigation
  @State
  list: WeChatItem[] = [{
    title: '朋友圈',
    icon: $r("sys.media.ohos_ic_public_arrow_right"),
    isDivider: true,
    toName: 'friend'
  },
    {
      title: '视频号',
      icon: $r("sys.media.ohos_ic_public_arrow_right"),
      toName: 'video'
    }, {
      title: '直播',
      icon: $r("sys.media.ohos_ic_public_arrow_right"),
      isDivider: true,
      toName: 'live'
    }
    , {
      title: '扫一扫',
      icon: $r("sys.media.ohos_ic_public_arrow_right"),
      toName: 'scan'
    }, {
      title: '听一听',
      icon: $r("sys.media.ohos_ic_public_arrow_right"),
      isDivider: true,
      toName: 'listen'
    }
    , {
      title: '看一看',
      icon: $r("sys.media.ohos_ic_public_arrow_right"),
      toName: 'look'
    }, {
      title: '搜一搜',
      icon: $r("sys.media.ohos_ic_public_arrow_right"),
      isDivider: true,
      toName: 'search'
    }]

  @Builder
  getNavBarMenu() {
    Column() {
      ForEach(this.list, (item: WeChatItem) => {
        Row() {
          Text(item.title)
          Image(item.icon)
            .width(30)
            .aspectRatio(1)
            .fillColor("#c6c6c6")
        }
        .padding({
          left: 20,
          right: 20
        })
        .margin({
          bottom: item.isDivider ? 10 : 0
        })
        .border({
          color: "#eaeaea",
          width: {
            bottom: 1
          }
        })
        .onClick(() => {
          // 完成跳转
          this.stackPath.pushPathByName(item.toName, null)
          // item.toName
        })
        // .stateStyles({
        //   normal: {
        //     .backgroundColor(Color.White)
        //   },
        //   pressed: {
        //     .backgroundColor(Color.Blue)
        //   }
        // })
        .height(50)
        .backgroundColor(Color.White)
        .width("100%")
        .justifyContent(FlexAlign.SpaceBetween)
      })
    }
    .width("100%")
  }

  build() {
    Navigation(this.stackPath) {
      this.getNavBarMenu() // 获取导航结构

    }
    .title("")
    .titleMode(NavigationTitleMode.Mini)
    .height('100%')
    .width('100%')
    .backgroundColor("#eaeaea")
    .navDestination(RouterBuilder)
  }
}

class WeChatItem {
  title: string = ""
  icon: ResourceStr = ""
  isDivider?: boolean
  toName?: string
}
  • 实现公共的路由 创建一个RouterBuilder 构造器
import Friend from '../components/Friend'
import Listen from '../components/Listen'
import Live from '../components/SearchPage'
import LivePage from '../components/LivePage'
import Look from '../components/Look'
import Scan from '../components/Scan'
import VideoPage from '../components/VideoPage'
import SearchPage from '../components/SearchPage'

@Builder
export function RouterBuilder(name: string) {
  if (name === "friend") {
    Friend()
  } else if (name === "live") {
    LivePage()
  } else if (name === "video") {
    VideoPage()
  } else if (name === "look") {
    Look()
  } else if (name === "search") {
    SearchPage()
  } else if (name === "listen") {
    Listen()
  } else if (name === "scan") {
    Scan()
  }
}
  • 以其中一个 Friend组件的示例
import { CommonParams } from '../viewmodels'

@Component
struct Friend {
  @Consume
  stackPath: NavPathStack

  build() {
    NavDestination() {
      Text("朋友圈")
      Button("去搜一搜")
        .onClick(() => {
          let params: CommonParams = {
            id: 1,
            name: '张三'
          }
          this.stackPath.pushPath({
            name: 'search',
            param: params
          })
          // this.stackPath.replacePath({
          //   name: 'search'
          // })
        })
    }
    .onWillAppear(() => {
      console.log("friend_onWillAppear")
    })
    .onAppear(() => {
      console.log("friend_onAppear")
    })
    .onWillShow(() => {
      console.log("friend_onWillShow")
    })
    .onShown(() => {
      console.log("friend_onShown")
    })
    .onWillHide(() => {
      console.log("friend_onWillHide")
    })
    .onHidden(() => {
      console.log("friend_onHidden")
    })
    .onWillDisappear(() => {
      console.log("friend_onWillDisappear")
    })
    .onDisAppear(() => {
      console.log("friend_onDisAppear")
    })
    .title("朋友圈")
  }
}

export default Friend
  • 如果需要接受参数,以SearchPage组件为例子,接收参数的示例
import { CommonParams } from '../viewmodels'

@Component
struct SearchPage {
  @Consume
  stackPath: NavPathStack

  aboutToAppear(): void {
    const params = this.stackPath.getParamByName("search") as CommonParams[]
    AlertDialog.show({
      message: JSON.stringify(params[0].name)
    })
  }

  build() {
    NavDestination() {
      Text("搜索")
      Button("返回")
        .onClick(() => {
          this.stackPath.pop() // 返回
        })
    }
    .title("搜索")
  }
}

export default SearchPage

3. router的使用

3.1. 介绍

  • router的使用都是基于Entry修饰的组件,都是基于resources/base/profile/main-page.json中的路由配置来跳转的
  • 优点:Router模块通过不同的url地址,可以方便地进行页面路由,轻松地访问不同的页面

3.2. 使用方法

router提供下列的几个方法
● pushUrl -压栈
● replaceUrl-替换页面栈
● back-返回
● clear-清空之前页面栈
● getParams-获取参数
● getState-获取当前路由状态
● getLength-获取当前所有的路由长度
● 单例模式
● showAlertBeforeBackPage- (返回阻断)
  • 关键api pushUrl会在当前页面层级再加一层页面,不管是不是同一个页面,A -> B 相当于当前页面栈中存在两个页面 A和B , 鸿蒙系统最多页面栈为32
// 注意跳转的页面必须是Entry修饰的页面
Button("push跳转")
.onClick(() => {
  router.pushUrl({
    url: 'pages/03/RouterCase'
  })
})
  • replaceUrl :会替换当前页面,不管是不是同一个页面,替换之后相当于页面重新执行
 Button("replace跳转")
        .onClick(() => {
          router.replaceUrl({
            url: 'pages/03/RouterCase'
          })
        })
  • clear : 清空页面栈中的所有历史页面,仅保留当前页面作为栈顶页面。
router.clear()
  • back : 回到上一个页面- 回到上一个页面,上一个页面并不会重新初始化
 router.back()
  • getParams : 在跳转过程中,可以给指定页面传递参数,在pushUrl和replaceUrl的第二个参数 。back也可以传参数
Button("push跳转")
 .onClick(() => {
  router.pushUrl({
    url: 'pages/03/RouterCase02',
    params: {
      id: 1
    }
  })
})
  • 在接收页面通过getParams接收参数
AlertDialog.show({ message:  (router.getParams() as Params).id?.toString() })

值得注意的是所有的参数 不论传入和传出都是object,我们需要将其断言成我们想要的类型

  • getState : 获取当前页面的状态信息

AlertDialog.show({
  message: JSON.stringify(router.getState())
})
  • getLength :获取当前页面栈的数量
 AlertDialog.show({
      message: JSON.stringify(router.getLength())
    })

  • 单例模式

路由默认属于标准模式

push就是一直追加,不管你有没有加载这个页面

单例模式

比如你加载过A 在栈底放着 再去追加时 会把页面从栈底拿出 放到栈顶

  • 单例模式不会造成线程的浪费

假设 A-B-C 现在C现在要回到A,此时用push会变成 A-B-C-A, 用replace会变成A-B-A, 可以给pushUrl加上单例模式, 变成 B-C-A, 或者直接用replace变成 B-A, 或者跳转后clear变成 A

 router.pushUrl({
              url: 'pages/03/RouterCase'
            }, router.RouterMode.Single)
  • showAlertBeforeBackPage
 router.showAlertBeforeBackPage({
        message: '确定要退出吗'
      })

该方法只需要在返回之前执行一下即可

  • 不能获取点击了确定还是取消,由它本身进行处理