HarmonyOS鸿蒙NEXT优雅的路由跳转方案 ZRouter

HarmonyOS鸿蒙NEXT优雅的路由跳转方案 ZRouter

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

前言

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

  1. <del>Router 不推荐</del>
  2. Navigation 推荐

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

介绍

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

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](/user/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](/user/hzw)/zrouter'

[@Entry](/user/Entry)
[@Component](/user/Component)
export struct Index {

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

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

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

[@Route](/user/Route)({ name: 'Login'})
[@Component](/user/Component)
export struct Login {

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

  }
}

7. 效果

效果

启动页的设置流程

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

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

// entry/src/main/ets/pages/Index.ets
import { Route, ZRouter } from '[@hzw](/user/hzw)/zrouter'

[@Route](/user/Route)({ name: "Index" })
[@Entry](/user/Entry)
[@Component](/user/Component)
export struct Index {
  build(){
    // 获取NavPathStack实例对象
    Navigation(ZRouter.getNavStack()) {
      Column({ space: 12 }) {
        Button('跳转到登录').onClick(event => {
          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](/user/hzw)/zrouter";

[@Route](/user/Route)({ name: "Splash" })
[@Component](/user/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](/user/hzw)/zrouter";

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

4. 效果

效果

动画转场

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

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

也可以自定义转场动画。

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

全局动画

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

import {
  OpacityAnimateOptions,
  RotateAnimateOptions,
  Route,
  ScaleAnimateOptions,
  TranslateAnimationOptions,
  ZRouter
} from '[@hzw](/user/hzw)/zrouter'

[@Entry](/user/Entry)
[@Component](/user/Component)
export struct Index {
  build(){
    // 获取NavPathStack实例对象
    Navigation(ZRouter.getNavStack()) {
      Column({ space: 12 }) {
        Button('跳转到登录').onClick(event => {
          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](/user/hzw)/zrouter";

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

3. 查看效果

效果

单个页面动画

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

1. 容器页面

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

[@Entry](/user/Entry)
[@Component](/user/Component)
export struct Index {
  build(){
    // 获取NavPathStack实例对象
    Navigation(ZRouter.getNavStack()) {
      Column({ space: 12 }) {
        Button('跳转到登录').onClick(event => {
          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](/user/hzw)/zrouter";

[@Route](/user/Route)({ name: 'Login'})
[@Component](/user/Component)
export struct Login {
  build(){
    NavDestination(){
      Column({ space: 12 }){
        Text("登录的页面")
      }
    }
    .title('登录')
    .width('100%')
    .height('100%')
    .onReady(context => {
      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))

  }
}

3. 效果

效果

生命周期

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](/user/hzw)/zrouter'

[@Route](/user/Route)({ name: "Index" })
[@Entry](/user/Entry)
[@Component](/user/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 => {
          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");
  }
}

2. 在页面中使用

[@Route](/user/Route)({ name: "Index" })
[@Entry](/user/Entry)
[@Component](/user/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 => {
          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](/user/hzw)/zrouter'


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

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

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

  }
}

拦截器

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

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

拦截器

常规使用

1. 定义拦截器 class

// entry/src/main/ets/utils/GlobalNavigateInterceptor.ets
import {
  IGlobalNavigateInterceptor,
  DestinationInfo,
  InterceptorInfo,
  ZRouter,
  RedirectType,
} from "[@hzw](/user/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](/user/hzw)/zrouter'
import { GlobalNavigateInterceptor } from '../utils/GlobalNavigateInterceptor';


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

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

needLogin

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

1. 页面中使用

页面中使用

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

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

2. 拦截器中处理

拦截器中处理

携带参数跳转

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

1. 携带参数跳转 setParam

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


export interface IGoods {
  num: number
  name: string
}

[@Route](/user/Route)({ name: "Index" })
[@Entry](/user/Entry)
[@Component](/user/Component)
export struct Index {
  build(){
    // 获取NavPathStack实例对象
    Navigation(ZRouter.getNavStack()) {
      Column({ space: 12 }) {
        Button('跳转到登录').onClick(event => {
          // 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](/user/hzw)/zrouter";
import { IGoods } from "./Index";

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

    })

  }
}

跨模块跳转

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

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

项目中新建 hsp 模块-library

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

  "dependencies": {
    "[@hzw](/user/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](/user/hzw)/zrouter"

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

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

  "dependencies": {
    "[@hzw](/user/hzw)/zrouter": "^1.4.1",
    "library": "file:../library"
  },

6. 进行页面跳转

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

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

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

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

        Button('跳转到信息详情页面').onClick(event => {
          ZRouter.getInstance().push("Info")
        })
      }
    }
    .

更多关于HarmonyOS鸿蒙NEXT优雅的路由跳转方案 ZRouter的实战教程也可以访问 https://www.itying.com/category-93-b0.html

2 回复

在HarmonyOS鸿蒙NEXT中,ZRouter是基于ArkTS实现的路由框架,专为单页应用设计。它提供声明式路由配置,支持动态路由、拦截器和参数传递。ZRouter通过@Route装饰器标注页面路径,使用ZRouter.push("pages/HomePage")方法跳转。路由信息存储在ZRouterConfig中,可通过ZRouter.getParams()获取传递参数。该方案简化了页面间通信,无需依赖第三方库,性能优于传统导航方式。

更多关于HarmonyOS鸿蒙NEXT优雅的路由跳转方案 ZRouter的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


ZRouter确实是一个优秀的HarmonyOS Next路由解决方案,它基于官方推荐的Navigation进行了深度封装,提供了更便捷的开发体验。从技术实现角度来看,它有几个值得关注的亮点:

  1. 架构设计上采用了插件化思想,通过router-register-plugin实现编译时路由注册,避免了运行时反射带来的性能损耗。

  2. 动画系统设计精良,支持多种预置动画效果的同时,通过NavAnimationMgr提供了灵活的扩展机制,开发者可以自定义复杂动画效果。

  3. 拦截器链的实现采用了责任链模式,支持优先级设置和中断控制,这种设计非常适合实现AOP编程范式。

  4. 跨模块通信通过HSP动态加载机制实现,结合服务路由的设计,解决了模块化解耦的关键问题。

  5. 生命周期管理采用了观察者模式,将系统生命周期事件转化为更细粒度的回调,方便业务逻辑组织。

在实际项目中使用时,建议注意:

  • 对于复杂路由场景,合理规划stack管理

  • 动画效果要考虑性能影响

  • 拦截器逻辑要保持简洁高效

总体来说,ZRouter的架构设计和功能实现都达到了较高水平,是当前HarmonyOS生态中路由方案的最佳选择之一。

回到顶部