HarmonyOS Next 优雅的路由跳转方案 ZRouter

前言

目前鸿蒙应用开发中,官方的路由跳转方案是

  1. Router 不推荐
  2. Navigation 推荐

然后 HMRouter 和 ZRouter 都是基于 Navigation 做的封装。

介绍

ZRouter是一款轻量级且非侵入性的鸿蒙动态路由框架,目前在三方库上已有4,671 的下载量,也是鸿蒙生态中很流行的路由管理库。

image-20250512141246931

也是 gitcode 上 G_Star 项目

image-20250512141412706

ZRouter 已上架录入到华为鸿蒙生态伙伴组件专区

特性

ZRouter 具有以下优点:

  • 简单易用
  • 支持 V1 状态管理和 V2 状态管理;
  • 支持 API 链式调用,让 API 更简洁直观;
  • 支持 NavDestination 页面模板化
  • 注解参数支持使用静态常量,可跨模块定义;
  • 支持自定义与全局拦截器,可设优先级及中断逻辑,可实现页面重定向、登录验证等业务场景。
  • 支持服务路由,可实现 Har/Hsp 模块间的通信;
  • 支持全局及单个页面的生命周期函数管理,可使任意类都能享有与组件相同的生命周期特性,可实现页面埋点统计等业务场景;
  • 支持跨多级页面参数携带返回监听;
  • 支持自定义 URL 路径跳转,可在拦截器内自行解析 URL 实现业务逻辑;
  • 内置多种转场动画效果(平移、旋转、渐变、缩放、高斯模糊),并支持自定义动画;
  • 支持启动模式、混淆、嵌套 Navigation、Hap;
  • 支持第三方 Navigation 的使用本库 API;
  • 支持与您现有项目中的 Navigation 无缝融合,实现零成本向本库迁移;
  • 支持 ArkUI-X 跨平台上使用;
  • 未来计划:支持共享元素动画、持续优化

快速上手

1. 项目中配置router-register-plugin插件

// hvigor/hvigor-config.json5
  "dependencies": {
    "router-register-plugin":"1.3.2"
  },

2. 在模块(har、hsp、hap 都可以)的 hvigorfile.ts 文件导入 router-register-plugin 插件,如在 Entry 中进行配置

// entry/hvigorfile.ts
import { hapTasks } from "@ohos/hvigor-ohos-plugin";
import { routerRegisterPlugin, PluginConfig } from "router-register-plugin";
// 2、初始化配置
const config: PluginConfig = {
  scanDirs: ["src/main/ets/pages", "src/main/ets/views"], // 扫描的目录,如果不设置,默认是扫描src/main/ets目录
  logEnabled: true, // 查看日志
  viewNodeInfo: false, // 查看节点信息
  isAutoDeleteHistoryFiles: true, // 删除无用编译产物
  lifecycleObserverAttributeName: "xxx", // 可选,设置全局的生命周期实现类在组件上的属性名,默认值是lifecycleObserver
};
export default {
  system: hapTasks /* Built-in plugin of Hvigor. It cannot be modified. */,
  plugins: [routerRegisterPlugin(config)],
};

3. 在模块中,下载安装 ZRouter

ohpm install @hzw/zrouter

4. 在 EntryAbility 的 onCreate()方法中初始化 ZRouter

onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
    this.context.getApplicationContext().setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_NOT_SET);
     ZRouter.initialize((config) => {
          // config.isLoggingEnabled = BuildProfile.DEBUG
          config.isHSPModuleDependent = true
    })
  }

5. 将 page/Index.ets ,作为 ZRouter 的根容器,需要使用Navigation进行外层容器

import { Route, ZRouter } from '@hzw/zrouter'

@Entry
@Component
export struct Index {

  build() {
    // 获取NavPathStack实例对象
    Navigation(ZRouter.getNavStack()){
      Column({space:12}){
        Button('跳转到登录').onClick((event: ClickEvent) => {
          ZRouter.getInstance().push("Login")
        })
      }
    }
    .title('首页')
    .height('100%')
    .width('100%')
  }
}

6. 其他页面需要使用@Route 和 NavDestination 进行修饰

// entry/src/main/ets/pages/Login.ets
import { Route, ZRouter } from "@hzw/zrouter";

@Route({ name: 'Login'})
@Component
export struct Login {

  build() {
    NavDestination(){
      Column({space:12}){
         Text("登录的页面")
          // 返回使用pop方法
      }
    }
    .title('登录')
    .width('100%')
    .height('100%')

  }
}

7. 效果

PixPin_2025-05-12_14-49-23

启动页的设置流程

一般 app 的启动流程都是 启动页面->主页,这里因为使用了 pages/Index.ets 作为了根容器,所以当要实现启动页流程时,其实是需要用到 3 个页面。

1. page/Index.ets 作为页面根容器

// entry/src/main/ets/pages/Index.ets
import { Route, ZRouter } from '@hzw/zrouter'

@Route({ name: "Index" })
@Entry
@Component
export struct Index {
  build() {
    // 获取NavPathStack实例对象
    Navigation(ZRouter.getNavStack()) {
      Column({ space: 12 }) {
        Button('跳转到登录').onClick((event: ClickEvent) => {
          ZRouter.getInstance().push("Login")
        })
      }
    }
    .title('首页')
    .height('100%')
    .width('100%')
    .onAppear(() => {
      // 跳转启动页
      ZRouter.getInstance().push("Splash")
    })
    .hideTitleBar(true)

  }
}

2. 新建一个启动页 ,完成页面展示后,跳转到真正的业务首页

// entry/src/main/ets/pages/Splash.ets
import { Route, ZRouter } from "@hzw/zrouter";

@Route({ name: "Splash" })
@Component
export struct Splash {
  build() {
    NavDestination() {
      Column() {
        Button('Splash 启动页')
      }
      .width("100%")
      .height("100%")
      .justifyContent(FlexAlign.Center)
      .backgroundColor(Color.Yellow)
    }
    .onAppear(() => {
      // 模拟展示启动页
      setTimeout(() => {
        ZRouter.getInstance().navigation("Login")
      }, 3000)
    })
  }
}

3. 业务首页

登录页面作为,业务中的首页

// entry/src/main/ets/pages/Login.ets
import { Route, TranslateAnimationOptions, ZRouter } from "@hzw/zrouter";

@Route({ name: 'Login' })
@Component
export struct Login {
  build() {
    NavDestination() {
      Column({ space: 12 }) {
        Text("登录的页面")
      }
    }
    .title('登录')
    .width('100%')
    .height('100%')
  }
}

4. 效果

PixPin_2025-05-12_17-40-53

动画转场

ZRouter 也提供了页面切换的动画效果,主要有:

  1. 平移
  2. 缩放
  3. 旋转
  4. 渐变

也可以自定义转场动画。

可以全局设置页面切换的动画,也可以单个页面设置。

全局动画

1. 在容器页面设置全局动画

import {
  OpacityAnimateOptions,
  RotateAnimateOptions,
  Route,
  ScaleAnimateOptions,
  TranslateAnimationOptions,
  ZRouter
} from '@hzw/zrouter'

@Entry
@Component
export struct Index {
  build() {
    // 获取NavPathStack实例对象
    Navigation(ZRouter.getNavStack()) {
      Column({ space: 12 }) {
        Button('跳转到登录').onClick((event: ClickEvent) => {
          ZRouter.getInstance().push("Login")
        })
      }
    }
    .title('首页')
    .height('100%')
    .width('100%')
    .customNavContentTransition(
      ZRouter.animateMgr()// 创建默认动画管理器
        .defaultAnimateBuilder()// 设置进场动画参数
        .setEnterAnimate({ duration: 500 })// 设置退场动画参数
        .setExitAnimate({ duration: 700 })// 平移
          // .addAnimateOptions(new TranslateAnimationOptions({ x: '50%', y: '50%' }))// 渐变
        .addAnimateOptions(new OpacityAnimateOptions(0.01))// 缩放
          // .addAnimateOptions(new ScaleAnimateOptions({ x: 0, y: 0 }))// 旋转
          // .addAnimateOptions(new RotateAnimateOptions({ angle: 90 }))// 自定义转场动画回调
        .getAnimCustomNavContentTransition())

  }
}

2. 在子页面注册和卸载动画

比如,需要跳转到登录页面,那么就在登录页面注册和卸载动画

// entry/src/main/ets/pages/Login.ets
import { Route, ZRouter } from "@hzw/zrouter";

@Route({ name: 'Login' })
@Component
export struct Login {
  build() {
    NavDestination() {
      Column({ space: 12 }) {
        Text("登录的页面")
      }
    }
    .title('登录')
    .width('100%')
    .height('100%')
    .onReady((context: NavDestinationContext) => {
      // 注册动画
      ZRouter.animateMgr().registerAnimParam(this, context)
    })
    .onDisAppear(() => {
      // 取消注册动画
      ZRouter.animateMgr().unregisterAnim(this)
    }) // 设置NavAnimationModifier
    .attributeModifier(ZRouter.animateMgr().modifier(this))
  }
}

3. 查看效果

PixPin_2025-05-12_15-47-09

单个页面动画

如果不想进行全局的页面跳转动画设置,可以给单个子页面进行设置,比如Login 页面

设置动画效果 和 注册、卸载动画函数

1. 容器页面

// entry/src/main/ets/pages/Index.ets
import {
  OpacityAnimateOptions,
  RotateAnimateOptions,
  Route,
  ScaleAnimateOptions,
  TranslateAnimationOptions,
  ZRouter
} from '@hzw/zrouter'

@Entry
@Component
export struct Index {
  build() {
    // 获取NavPathStack实例对象
    Navigation(ZRouter.getNavStack()) {
      Column({ space: 12 }) {
        Button('跳转到登录').onClick((event: ClickEvent) => {
          ZRouter.getInstance().push("Login")
        })
      }
    }
    .title('首页')
    .height('100%')
    .width('100%')
    // 容器页面设置
    .customNavContentTransition(
      ZRouter
        .animateMgr()
        .getAnimCustomNavContentTransition())
  }
}

2. 登录页面

// entry/src/main/ets/pages/Login.ets
import { Route, TranslateAnimationOptions, ZRouter } from "@hzw/zrouter";

@Route({ name: 'Login' })
@Component
export struct Login {
  build() {
    NavDestination() {
      Column({ space: 12 }) {
        Text("登录的页面")
      }
    }
    .title('登录')
    .width('100%')
    .height('100%')
    .onReady((context: NavDestinationContext) => {
      ZRouter
        .animateMgr()
        .registerAnimParam(this, context)// 设置进场动画参数(比如动画时间,播放速度等)
        .setEnterAnimate({ duration: 500, curve: Curve.LinearOutSlowIn })// 设置退场动画参数(比如动画时间,播放速度等)
        .setExitAnimate({ duration: 500, curve: Curve.LinearOutSlowIn })// 添加平移动画
        .addAnimateOptions(new TranslateAnimationOptions({ y: '100%' }))
    })
    .onDisAppear(() => {
      // 取消注册动画
      ZRouter.animateMgr().unregisterAnim(this)
    }) // 设置NavAnimationModifier
    .attributeModifier(ZRouter.animateMgr().modifier(this))

  }
}
  1. 效果

PixPin_2025-05-12_15-47-09

生命周期

ZRouter 也提供了项目开发必备的生命周期

名称 解释
aboutToAppear 即将出现
aboutToDisappear 即将消失
onWillAppear 在即将出现时
onAppear 在出现时
onWillShow 在即将展示时
onShown 在已展示时
onWillHide 在即将隐藏时
onHidden 在已隐藏时
onWillDisappear 在即将消失时
onDisappear 在消失时

ZRouter 中提供了3 种使用生命周期的方式,差别不大,分别是字面量class的以及直接判断

字面量

在需要使用生命周期的页面中,进行注册生命周期

// entry/src/main/ets/pages/Index.ets:1
import { ILifecycleObserver, Lifecycle, LifecycleRegistry, Route, ZRouter } from '@hzw/zrouter'

@Route({ name: "Index" })
@Entry
@Component
export struct Index {
  // 1 声明生命周期注册器
  @Lifecycle lifecycle: LifecycleRegistry = LifecycleRegistry.create(this);
  tag = "Index "

  aboutToAppear(): void {
    // 2、使用addObserver设置生命周期函数监听
    let that = this
    this.lifecycle.addObserver<ILifecycleObserver>({
      aboutToAppear: () => {
        console.log(`${that.tag}aboutToAppear`)
      },
      aboutToDisappear: () => {
        console.log(`${that.tag}aboutToDisappear`)
      },
      onWillAppear(r) {
        console.log(`${that.tag}onWillAppear`, r?.name, r?.navDestinationId)
      },
      onAppear(r) {
        console.log(`${that.tag}onAppear`, r?.name, r?.navDestinationId)
      },
      onWillShow(r) {
        console.log(`${that.tag}onWillShow `, r?.name, r?.navDestinationId)
      },
      onShown(r) {
        console.log(`${that.tag}onShown `, r?.name, r?.navDestinationId)
      },
      onWillHide(r) {
        console.log(`${that.tag}onWillHide `, r?.name, r?.navDestinationId)
      },
      onHidden(r) {
        console.log(`${that.tag}onHidden `, r?.name, r?.navDestinationId)
      },
      onWillDisappear(r) {
        console.log(`${that.tag}onWillDisappear `, r?.name, r?.navDestinationId)
      },
      onDisappear(r) {
        console.log(`${that.tag}onDisappear `, r?.name, r?.navDestinationId)
      }
    })
  }

  build() {
    // 获取NavPathStack实例对象
    Navigation(ZRouter.getNavStack()) {
      Column({ space: 12 }) {
        Button('跳转到登录').onClick((event: ClickEvent) => {
          ZRouter.getInstance().push("Login")
        })
      }
    }
    .title('首页')
    .height('100%')
    .width('100%')

  }
}

class 的方式

  1. 用 class 的方式定义 class
// 1 定义生命周期class
@Observed
export class LifecycleModel implements ILifecycleObserver {
  private tag = "class的生命周期: ";
  loadState: string = "请求数据中...";
  counter: number = 0;

  aboutToAppear(): void {
    console.log(this.tag + "aboutToAppear");
  }

  aboutToDisappear(): void {
    console.log(this.tag + "aboutToDisappear");
  }

  onWillAppear(info: RouterInfo): void {
    console.log(this.tag + "onWillAppear");
  }

  onAppear(info: RouterInfo): void {
    console.log(this.tag + "onAppear");
    // 模拟请求数据
    setTimeout(() => {
      this.counter = 100;
    }, 3000);
  }

  onWillShow(info: RouterInfo): void {
    console.log(this.tag + "onWillShow");
  }

  onShown(info: RouterInfo): void {
    console.log(this.tag + "onShown");
  }

  onWillHide(info: RouterInfo): void {
    console.log(this.tag + "onWillHide");
  }

  onHidden(info: RouterInfo): void {
    console.log(this.tag + "onHidden");
  }

  onWillDisappear(info: RouterInfo): void {
    console.log(this.tag + "onWillDisappear");
  }

  onDisappear(info: RouterInfo): void {
    console.log(this.tag + "onDisappear");
  }
}
  1. 在页面中使用
@Route({ name: "Index" })
@Entry
@Component
export struct Index {
  // 2 使用生命周期class
  @State viewModel: LifecycleModel = new LifecycleModel()
  @Lifecycle lifecycle: LifecycleRegistry = LifecycleRegistry.create(this);

  aboutToAppear(): void {
    // 3 注册监听
    this.lifecycle.addObserver(this.viewModel)
  }

  build() {
    // 获取NavPathStack实例对象
    Navigation(ZRouter.getNavStack()) {
      Column({ space: 12 }) {
        Button('跳转到登录').onClick((event: ClickEvent) => {
          ZRouter.getInstance().push("Login")
        })
      }
    }
    .title('首页')
    .height('100%')
    .width('100%')

  }
}

直接判断

通过 this.lifecycle.addListener 直接判断。

// entry/src/main/ets/pages/Index.ets:1
import { Lifecycle, LifecycleRegistry, Route, ZRouter } from '@hzw/zrouter'


@Route({ name: "Index" })
@Entry
@Component
export struct Index {
  @Lifecycle lifecycle: LifecycleRegistry = LifecycleRegistry.create(this);

  aboutToAppear(): void {
    // 直接判定
    this.lifecycle.addListener((pageName, r) => {
      // pageName 生命周期函数的名字  aboutToAppear、onPageShow等
      console.log('--2--', pageName, r?.name ?? "", r?.navDestinationId ?? "")
    })
  }

  build() {
    // 获取NavPathStack实例对象
    Navigation(ZRouter.getNavStack()) {
      Column({ space: 12 }) {
        Button('跳转到登录').onClick((event: ClickEvent) => {
          ZRouter.getInstance().push("Login")
        })
      }
    }
    .title('首页')
    .height('100%')
    .width('100%')

  }
}

拦截器

ZRouter 支持多个拦截器和全局拦截器,在拦截器中可以做页面跳转的拦截,比如跳转前拦截、数据预取、登录拦截,404 拦截、埋点、自定义 URL 路径等等。

拦截器 解释
onNavigateBefore 在跳转之前回调
onRootWillShow Navigation 根视图显示时回调
onPageWillShow 页面显示时回调
onNavigate 页面 push 或者 replace 跳转时回调
onShowCallback 页面显示回调

image-20250512172354900

常规使用

1. 定义拦截器 class

// entry/src/main/ets/utils/GlobalNavigateInterceptor.ets
import {
  IGlobalNavigateInterceptor,
  DestinationInfo,
  InterceptorInfo,
  ZRouter,
  RedirectType,
} from "@hzw/zrouter";
import { promptAction } from "@kit.ArkUI";

export class GlobalNavigateInterceptor implements IGlobalNavigateInterceptor {
  static count = 0;
  /**
   * 在跳转之前回调,可以在此回调中拦截跳转做一些自定义的逻辑,比如修改路由参数、数据预取、拦截跳转、拦截登录等场景
   * @param dest
   * @returns DestinationInfo#action 为NavigationAction.BLOCK 则表示拦截跳转,NEXT继续执行
   * @note
   * 如果通过ZRouter.getNavStack().push()方法跳转,则不会回调此方法,后续会考虑兼容
   * 只有通过ZRouter.getInstance().push()方法跳转时会回调此方法
   */
  onNavigateBefore: (destInfo: DestinationInfo) => Promise<DestinationInfo> = (
    destInfo
  ) => {
    console.log("IInterceptor Global onNavigateBefore -> ", destInfo.name);
    return new Promise((resolve, _) => {
      // 拦截页面1
      if (destInfo.name === "Login") {
        console.log("拦截了登录页面");
        // 拦截跳转到Login页面
        if (GlobalNavigateInterceptor.count === 0) {
          // 自己定义的一些逻辑
          destInfo.param = " 在拦截器onNavigateBefore中已替换参数 ";
          destInfo.next(); // 继续跳转 默认的 ,可以不写
        }
      }
      resolve(destInfo);
    });
  };
  /**
   * Navigation根视图显示时回调
   * @param fromContext
   */
  onRootWillShow: ((fromContext: NavDestinationContext) => void) | undefined = (
    fromContext
  ) => {
    console.log(
      "IInterceptor Global onRootWillShow: ",
      fromContext.pathInfo.name
    );
  };
  /**
   * 页面显示时回调,当fromContext、toContext是一样时,则在NavBar根页面中启动的
   * @param fromContext
   * @param toContext
   */
  onPageWillShow:
    | ((
        fromContext: NavDestinationContext,
        toContext: NavDestinationContext
      ) => void)
    | undefined = (from, to) => {
    console.log(
      "IInterceptor Global onPageWillShow: ",
      from,
      to.pathInfo.name,
      to.pathInfo.param
    );
  };
  /**
   * 页面push 或者 replace跳转时回调
   * @param context
   */
  onNavigate: ((context: InterceptorInfo) => void) | undefined = (info) => {
    if (info.notRegistered) {
      return;
    }
    console.log("IInterceptor Global onNavigate -> ", info.name);
    let isLogin = AppStorage.get<boolean>("isLogin");
    if (info.isNeedLogin && !isLogin) {
      let param = info.param;
      ZRouter.getInstance()
        .setParam(param)
        .setAnimate(true)
        .setPopListener((result) => {
          if (result.data) {
            //  登录成功
            promptAction.showToast({ message: `登录成功` });
            return true; // 返回true 则继续跳转登录前的页面
          } else {
            return false;
          }
        })
        .redirect("LoginPage", RedirectType.REPLACE);
    }
  };
  /**
   * 页面显示回调,鸿蒙sdk默认的,没有处理逻辑,如果其他的回调函数无法满足你的需求,可考虑在这里实现
   * @param context
   */
  onShowCallback: InterceptionShowCallback = (info) => {};
}

2. 注册使用

// entry/src/main/ets/pages/Index.ets:1
import { Route, ZRouter } from '@hzw/zrouter'
import { GlobalNavigateInterceptor } from '../utils/GlobalNavigateInterceptor';


@Route({ name: "Index" })
@Entry
@Component
export struct Index {
  aboutToAppear(): void {
    // 添加拦截器
    ZRouter.setGlobalInterceptor(new GlobalNavigateInterceptor());
  }

  build() {
    // 获取NavPathStack实例对象
    Navigation(ZRouter.getNavStack()) {
      Column({ space: 12 }) {
        Button('跳转到登录').onClick((event: ClickEvent) => {
            // 此时会触发拦截器
          ZRouter.getInstance().push("Login")
        })
      }
    }
    .title('首页')
    .height('100%')
    .width('100%')
  }
}

needLogin

普通的页面在使用装饰器 @Route 的时候,也可以传递一个属性needlogin:boolean,该属性会在拦截器中出现,我们可以结合该属性进行登录的权限校验。

1. 页面中使用

image-20250512190304099

然后当跳转到这个页面时,拦截器中的就可以获取到该属性

ZRouter.getInstance().push("Cart")

2. 拦截器中处理

image-20250512190623863

携带参数跳转

跳转页面的时候携带参数的写法比较简单

1. 携带参数跳转 setParam

// entry/src/main/ets/pages/Index.ets:1
import { Route, ZRouter } from '@hzw/zrouter'


export interface IGoods {
  num: number
  name: string
}


@Route({ name: "Index" })
@Entry
@Component
export struct Index {
  build() {
    // 获取NavPathStack实例对象
    Navigation(ZRouter.getNavStack()) {
      Column({ space: 12 }) {
        Button('跳转到登录').onClick((event: ClickEvent) => {
          // ZRouter.getInstance().push("Login")
          ZRouter.getInstance()
            .setParam({ num: 100, name: "手机" })
            .push("Login")
        })
      }
    }
    .title('首页')
    .height('100%')
    .width('100%')

  }
}

2. 获取页面参数 getParam

// entry/src/main/ets/pages/Login.ets
import { Route, TranslateAnimationOptions, ZRouter } from "@hzw/zrouter";
import { IGoods } from "./Index";

@Route({ name: 'Login' })
@Component
export struct Login {
  build() {
    NavDestination() {
      Column({ space: 12 }) {
        Text("登录的页面")
      }
    }
    .title('登录')
    .width('100%')
    .height('100%')
    .onReady((context: NavDestinationContext) => {
      const param = ZRouter.getInstance().getParam() as IGoods
      console.log("参数", param.num, param.name)

    })

  }
}

跨模块跳转

这里演示两个模块,一个是hap-entry,另外一个是hsp-library,演示entry跳转到 library 模块。

1. 项目中新建 hsp 模块-library

image-20250512211855945

2. library 模块下载 ZRouter library/oh-package.json5

  "dependencies": {
    "@hzw/zrouter": "^1.4.1",
  }

3. library模块在 hvigorfile.ts 导入 router-register-plugin

// library/hvigorfile.ts
import { hspTasks } from "@ohos/hvigor-ohos-plugin";
import { routerRegisterPlugin, PluginConfig } from "router-register-plugin";

// 2、初始化配置
const config: PluginConfig = {
  scanDirs: ["src/main/ets/pages", "src/main/ets/views"], // 扫描的目录,如果不设置,默认是扫描src/main/ets目录
  logEnabled: true, // 查看日志
  viewNodeInfo: false, // 查看节点信息
  isAutoDeleteHistoryFiles: true, // 删除无用编译产物
  lifecycleObserverAttributeName: "xxx", // 可选,设置全局的生命周期实现类在组件上的属性名,默认值是lifecycleObserver
};

export default {
  system: hspTasks /* Built-in plugin of Hvigor. It cannot be modified. */,
  plugins: [
    routerRegisterPlugin(config),
  ] /* Custom plugin to extend the functionality of Hvigor. */,
};

4. library模块新建 Info 页面

// library/src/main/ets/pages/Info.ets
import { Route } from "@hzw/zrouter"

@Route({ name: "Info" })
@Component
export struct Info {
  build() {
    NavDestination() {
      Column() {
        Button('Info')
      }
      .width("100%")
      .height("100%")
      .justifyContent(FlexAlign.Center)
    }
  }
}

5. entry 模块引入其他模块 entry/oh-package.json5

  "dependencies": {
    "@hzw/zrouter": "^1.4.1",
    "library": "file:../library"
  },

6. 进行页面跳转

// entry/src/main/ets/pages/Index.ets:1
import { Route, ZRouter } from '@hzw/zrouter'
import { GlobalNavigateInterceptor } from '../utils/GlobalNavigateInterceptor';

export interface I13 {
  id: number;
  name: string
}

@Route({ name: "Index" })
@Entry
@Component
export struct Index {
  aboutToAppear(): void {
    // 添加拦截器
    ZRouter.setGlobalInterceptor(new GlobalNavigateInterceptor());
  }

  build() {
    // 获取NavPathStack实例对象
    Navigation(ZRouter.getNavStack()) {
      Column({ space: 12 }) {
        Button('跳转到登录').onClick((event: ClickEvent) => {
          ZRouter.getInstance().push("Login")
        })
        Button('跳转到购物车').onClick((event: ClickEvent) => {
          ZRouter.getInstance().push("Cart")
        })
        Button("跳转到商品详情页面")
          .onClick(() => {
            ZRouter.getInstance().setParam({
              id: 1,
              name: "商品1"
            })
              .push("Goods")
          })

        Button('跳转到信息详情页面').onClick((event: ClickEvent) => {
          ZRouter.getInstance().push("Info")
        })
      }
    }
    .title('首页')
    .height('100%')
    .width('100%')

  }
}

7. 效果

PixPin_2025-05-12_21-28-06

NavDestination 页面模板化能力

页面模板能力可以让我们不需要每一个页面都添加 NavNavigation 组件,提高我们的开发效率。

@Route 注解中使用 useTemplate 即可使用页面模板化能力,如:

import { Route } from "@hzw/zrouter"

@Route({ name: "Demo", useTemplate: true, title: "DemoTitle" })
@Component
export struct Demo {
  build() {
    Column() {
      Button('Demo')
    }
    .width("100%")
    .height("100%")
    .justifyContent(FlexAlign.Center)
  }
}

接口列表

  • ZRouter: 是路由管理器,包括了初始化、路由操作、服务路由、生命周期、拦截器、模板化、动画等功能的管理
  • NavDestBuilder: 路由的进出栈管理,主要包括跳转、返回、替换、移除等操作。

ZRouter 类接口

方法名 描述 参数 返回值 废弃状态
initialize 初始化路由,可传入一个配置函数 invoke?: ((config: ConfigInitializer) => void) void
setModuleLoadedListener 设置模块加载完成的监听器,在使用了服务路由会生效 listener: () => void void
isDynamicLoadedComplete 判断模块是否动态加载完成 boolean
getInstance 获取路由操作的实例,用于进行跳转、返回等操作 stackName: string = DEFAULT_STACK_NAME NavDestBuilder<ObjectOrNull>
addService 添加服务路由,外部不需要手动调用,会在编译阶段自动注册 name: string, service: IProvider void
getService 获取服务路由管理类 name: string T extends IProvider null
animateMgr 获取动画管理类 NavAnimationMgr
registerNavStack 注册路由栈 stackName: string = DEFAULT_STACK_NAME, pathStack: NavPathStack void
unregisterNavStack 解除注册路由栈 stackName: string void
getNavStack 获取默认栈名的路由栈 willShow?: InterceptionShowCallback NavPathStack
getNavStackByName 根据栈名获取路由栈,通常用不上 stackName: string NavPathStack
getCurrentNavStack 获取当前路由栈实例 NavPathStack
getLastNavDestinationId 获取当前路由栈的栈顶页面 DestinationId string or undefined
setGlobalInterceptor 设置全局拦截器 interceptor: IGlobalNavigateInterceptor, stackName: string = DEFAULT_STACK_NAME void
setInterceptor 添加拦截器 interceptor: T extends IInterceptor, stackName: string = DEFAULT_STACK_NAME void
removeInterceptor 移除拦截器 interceptor: T extends IInterceptor, stackName: string = DEFAULT_STACK_NAME boolean
addGlobalLifecycleObserver 添加全局的 NavDestination 页面的生命周期观察者 observer: IL extends ILifecycleObserver 未明确
addLifecycleObserver 添加单个 NavDestination 页面的生命周期观察者 observer: LifecycleObserver 未明确
templateMgr 获取 NavDestination 页面模板管理器,通常用不上 TemplateMgr
getCurrentStackName 获取当前路由栈名称 string
getParamByKey 获取路由参数,建议带上默认值和泛型 key: string, defVal: ObjectOrNull = null P = ObjectOrNull

NavDestBuilder 类接口

方法名 描述 参数 返回值 废弃状态
setAnimate 设置是否启动动画 animated: boolean NavDestBuilder<T>
setLaunchMode 设置启动模式,api12 起才会生效 mode: LaunchMode NavDestBuilder<T>
setParam 设置页面跳转携带的参数,通过getParam获取参数 param: ObjectOrNull NavDestBuilder<T>
withParam 设置页面跳转携带的参数,key-value 形式,建议使用,通过ZRouter.getParamByKey(key)获取参数 key: string, value: ObjectOrNull NavDestBuilder<T>
setPopListener 监听页面返回携带的结果,所有页面返回结果都会回调着 callback: OnPopResultCallback<TObjectOrNull> NavDestBuilder<T>

总结

ZRouter 作为鸿蒙生态中流行的路由管理库,为 HarmonyOS Next 应用提供了简洁而强大的路由解决方案。它的主要优势包括:

  1. 简单易用:ZRouter 提供链式 API 调用,使代码更简洁直观,降低开发者的使用门槛。

  2. 丰富的动画效果:内置多种转场动画(平移、旋转、渐变、缩放等),并支持自定义,使页面切换更加流畅美观。

  3. 强大的拦截器机制:可实现页面重定向、登录验证、埋点统计等复杂业务场景。

  4. 完善的生命周期管理:提供丰富的生命周期函数,让任意类都能享有与组件相同的生命周期特性。

  5. 跨模块通信能力:支持服务路由,实现 Har/Hsp 模块间的无缝通信。

  6. 页面模板化:通过@Route 注解和 useTemplate 配置,提高开发效率和代码复用率。

  7. 兼容性和可扩展性:支持与现有 Navigation 无缝融合,实现零成本迁移;适用于 ArkUI-X 跨平台开发。

ZRouter 的设计思路遵循简洁、高效、可扩展的原则,为鸿蒙应用开发者提供了一套完整的路由解决方案,极大地简化了页面跳转、参数传递和页面生命周期管理的复杂性,是构建大型 HarmonyOS 应用的理想选择。

相关文件下载
ZRouterDemo.zip
5.21 MB
下载
Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐