ArkTS第三课:页面路由和导航

在这里插入图片描述

1. 页面路由概述

在HarmonyOS应用开发中,页面路由和导航是实现多页面应用的核心机制。良好的路由管理可以提供流畅的用户体验,简化页面间的数据传递和状态管理。ArkTS提供了多种路由实现方式,包括原生路由能力和第三方路由框架。

1.1 路由的基本概念

  • 页面路由:指应用中不同页面之间的切换和跳转机制
  • 导航栈:管理页面层级关系的后进先出(LIFO)数据结构
  • 页面参数传递:在页面跳转过程中携带和接收数据的方式
  • 路由拦截:在页面跳转前后执行自定义逻辑的能力

11.2 ArkTS中的路由方案对比

路由方案优点缺点适用场景
原生路由Router系统内置,轻量简单功能相对基础,缺乏高级特性简单的页面跳转场景
Navigator组件声明式导航,与UI结合紧密灵活性受限,不支持复杂的路由管理简单的页面间导航
HMRouter框架功能丰富,支持声明式配置,动画效果好需要额外安装依赖复杂应用、需要高级路由特性的场景

2. 原生路由Router的使用

HarmonyOS提供了内置的@ohos.router模块,用于实现基本的页面跳转和导航功能。

2.1 Router模块的基本用法

首先,需要导入Router模块:

import router from '@ohos.router';
页面跳转(push操作)
// 不带参数的页面跳转
router.push({
  url: 'pages/DetailPage'
});

// 带参数的页面跳转
router.push({
  url: 'pages/DetailPage',
  params: {
    itemId: '123',
    itemName: '示例商品'
  }
});
返回上一页(back操作)
// 返回上一页
router.back();

// 带返回参数的返回
router.back({
  params: {
    result: 'success',
    data: {}
  }
});
替换当前页(replace操作)
// 替换当前页面,用户无法返回到被替换的页面
router.replace({
  url: 'pages/LoginPage'
});

2.2 页面参数的接收和处理

在目标页面中,可以通过router.getParams()方法获取从上一个页面传递过来的参数:

// 在目标页面中获取参数
import router from '@ohos.router';

@Entry
@Component
export struct DetailPage {
  private itemId: string = '';
  private itemName: string = '';

  onPageShow() {
    // 获取路由参数
    const params = router.getParams();
    if (params) {
      this.itemId = params.itemId as string;
      this.itemName = params.itemName as string;
    }
  }

  build() {
    Column() {
      Text(`商品ID: ${this.itemId}`)
      Text(`商品名称: ${this.itemName}`)
    }
  }
}

2.3 页面生命周期与路由事件

在使用路由时,需要了解页面的生命周期事件:

@Entry
@Component
export struct MyPage {
  // 页面创建时调用
  onCreate() {
    console.info('页面创建');
  }
  
  // 页面显示时调用
  onPageShow() {
    console.info('页面显示');
    // 可以在这里获取路由参数或刷新数据
  }
  
  // 页面隐藏时调用
  onPageHide() {
    console.info('页面隐藏');
  }
  
  // 页面销毁时调用
  onDestroy() {
    console.info('页面销毁');
    // 在这里释放资源
  }

  build() {
    // UI组件定义
  }
}

3. HMRouter路由框架详解

HMRouter是HarmonyOS生态中功能丰富的第三方路由框架,提供了声明式配置、强大的动画效果和灵活的导航管理能力。

3.1 HMRouter的安装与配置

首先,通过OHPM安装HMRouter框架:

ohpm install @hadss/hmrouter @hadss/hmrouter-transitions

安装完成后,在项目的oh-package.json5中会自动添加依赖:

{
  "dependencies": {
    "@hadss/hmrouter": "^1.2.0-rc.0",
    "@hadss/hmrouter-transitions": "^1.2.0-rc.0"
  }
}

3.2 HMRouter的初始化

在应用的入口Ability中初始化HMRouter:

// EntryAbility.ets
import { HMRouterMgr } from '@hadss/hmrouter';
import UIAbility from '@ohos.app.ability.UIAbility';
import window from '@ohos.window';

export default class EntryAbility extends UIAbility {
  onCreate(want, launchParam) {
    // 初始化HMRouter
    HMRouterMgr.init({
      context: this.context,
    });
    console.log('EntryAbility onCreate');
  }

  onWindowStageCreate(windowStage: window.WindowStage) {
    // 设置WindowStage
    windowStage.loadContent('pages/Index', (err, data) => {
      if (err.code) {
        console.error('Failed to load the content. Cause: ' + JSON.stringify(err));
        return;
      }
      console.log('Succeeded in loading the content. Data: ' + JSON.stringify(data));
    });
  }
}

3.3 页面注册与配置

使用@HMRouter装饰器注册页面:

// 页面路径常量管理
// constants/PagePath.ets
export class PagePath {
  static readonly INDEX: string = 'pages/Index';
  static readonly DETAIL: string = 'pages/DetailPage';
  static readonly SETTINGS: string = 'pages/SettingsPage';
}

// 在页面组件中注册
// pages/DetailPage.ets
import { HMRouter } from '@hadss/hmrouter';
import { PagePath } from '../constants/PagePath';

@HMRouter({ pageUrl: PagePath.DETAIL })
@Entry
@Component
export struct DetailPage {
  // 页面内容
  build() {
    Column() {
      Text('详情页面')
    }
  }
}

3.4 使用HMRouter进行页面导航

基本页面跳转
import { HMRouterMgr } from '@hadss/hmrouter';
import { PagePath } from '../constants/PagePath';

// 页面跳转
HMRouterMgr.push({
  pageUrl: PagePath.DETAIL,
  param: {
    productId: '1001',
    productName: '测试产品'
  }
});
参数接收

在目标页面中,可以直接通过装饰器接收路由参数:

import { HMRouter, Param } from '@hadss/hmrouter';
import { PagePath } from '../constants/PagePath';

@HMRouter({ pageUrl: PagePath.DETAIL })
@Entry
@Component
export struct DetailPage {
  @Param productId?: string;
  @Param productName?: string;
  
  build() {
    Column() {
      Text(`产品ID: ${this.productId}`)
      Text(`产品名称: ${this.productName}`)
    }
  }
}
返回上一页
// 简单返回
HMRouterMgr.pop();

// 带返回参数的返回
HMRouterMgr.pop({
  param: {
    result: 'success',
    selectedItem: { id: '1', name: '选项1' }
  }
});
替换页面
HMRouterMgr.replace({
  pageUrl: PagePath.SETTINGS
});
清空导航栈并跳转
HMRouterMgr.replaceAll({
  pageUrl: PagePath.LOGIN
});

3.5 HMNavigation导航容器

HMRouter提供了HMNavigation组件,用于实现更高级的导航功能:

import { HMNavigation, HMDefaultGlobalAnimator } from '@hadss/hmrouter';

@Entry
@Component
export struct AppRouter {
  build() {
    HMNavigation({
      navigationId: 'MainNavigation',
      homePageUrl: 'pages/Index',
      options: {
        standardAnimator: HMDefaultGlobalAnimator.STANDARD_ANIMATOR,
        dialogAnimator: HMDefaultGlobalAnimator.DIALOG_ANIMATOR
      }
    })
  }
}

4. ArkTS中的状态管理与页面间数据传递

4.1 状态管理装饰器

在ArkTS中,有多种状态管理装饰器可用于页面间的数据传递和状态管理:

  1. @State:组件内的状态变量,变化时会触发UI刷新
  2. @Prop:父组件向子组件传递数据的单向绑定
  3. @Link:父子组件间的双向绑定
  4. @Provide/@Consume:跨层级组件间的状态传递
  5. @LocalStorage:本地存储状态,应用级别持久化
  6. @AppStorage:应用级全局状态管理

4.2 页面间数据传递方式

通过路由参数传递
// 发送页面
HMRouterMgr.push({
  pageUrl: PagePath.DETAIL,
  param: {
    userId: '12345',
    userName: '张三'
  }
});

// 接收页面
@HMRouter({ pageUrl: PagePath.DETAIL })
@Entry
@Component
export struct DetailPage {
  @Param userId?: string;
  @Param userName?: string;
  
  build() {
    Column() {
      Text(`用户ID: ${this.userId}`)
      Text(`用户名: ${this.userName}`)
    }
  }
}
通过全局状态传递
// 发送页面
@Entry
@Component
export struct SenderPage {
  onSendData() {
    // 使用AppStorage传递数据
    AppStorage.SetOrCreate('sharedData', {
      message: 'Hello from sender page',
      timestamp: Date.now()
    });
    
    // 跳转到接收页面
    HMRouterMgr.push({ pageUrl: PagePath.RECEIVER });
  }
}

// 接收页面
@HMRouter({ pageUrl: PagePath.RECEIVER })
@Entry
@Component
export struct ReceiverPage {
  @StorageLink('sharedData') sharedData: any = null;
  
  build() {
    Column() {
      if (this.sharedData) {
        Text(`接收到的数据: ${this.sharedData.message}`)
        Text(`时间戳: ${this.sharedData.timestamp}`)
      } else {
        Text('暂无数据')
      }
    }
  }
}
通过全局对象传递
// 在全局作用域定义数据
globalThis.familyTree = new FamilyTree();

// 在任意页面中访问和修改
@HMRouter({ pageUrl: PagePath.FAMILY_TREE })
@ComponentV2
export struct FamilyTreeView {
  @Local familyTree: FamilyTree = globalThis.familyTree || new FamilyTree()
  
  build() {
    // 使用familyTree数据
  }
}

5. 导航动画效果

5.1 HMRouter的内置动画

HMRouter提供了多种内置的过渡动画效果:

import { HMNavigation, HMDefaultGlobalAnimator } from '@hadss/hmrouter';
import { TransitionAnimatorType } from '@hadss/hmrouter/transition';

@Entry
@Component
export struct AppRouter {
  build() {
    HMNavigation({
      navigationId: 'MainNavigation',
      homePageUrl: 'pages/Index',
      options: {
        standardAnimator: HMDefaultGlobalAnimator.get(TransitionAnimatorType.FADE),
        dialogAnimator: HMDefaultGlobalAnimator.get(TransitionAnimatorType.SLIDE)
      }
    })
  }
}

5.2 自定义导航动画

可以通过HMRouter的动画配置实现自定义导航效果:

import { HMNavigation } from '@hadss/hmrouter';
import { TransitionAnimator } from '@hadss/hmrouter/transition';

@Entry
@Component
export struct AppRouter {
  // 自定义动画配置
  private customAnimator = new TransitionAnimator({
    push: {
      duration: 500,
      curve: 'ease',
      delay: 0,
      styles: {
        enter: {
          opacity: [0, 1],
          transform: [
            { translateX: ['100%', '0%'] }
          ]
        },
        exit: {
          opacity: [1, 0.8],
          transform: [
            { translateX: ['0%', '-30%'] }
          ]
        }
      }
    },
    pop: {
      duration: 500,
      curve: 'ease',
      delay: 0,
      styles: {
        enter: {
          opacity: [0.8, 1],
          transform: [
            { translateX: ['-30%', '0%'] }
          ]
        },
        exit: {
          opacity: [1, 0],
          transform: [
            { translateX: ['0%', '100%'] }
          ]
        }
      }
    }
  });

  build() {
    HMNavigation({
      navigationId: 'MainNavigation',
      homePageUrl: 'pages/Index',
      options: {
        standardAnimator: this.customAnimator
      }
    })
  }
}

6. 路由守卫与拦截

6.1 全局路由守卫

HMRouter支持全局路由守卫,可以在所有页面跳转前后执行自定义逻辑:

// EntryAbility.ets 中配置
import { HMRouterMgr, RouterGuard } from '@hadss/hmrouter';

onCreate(want, launchParam) {
  // 初始化HMRouter
  HMRouterMgr.init({
    context: this.context,
    // 全局路由守卫
    beforeEach: (to, from, next) => {
      console.log(`即将从${from.pageUrl}跳转到${to.pageUrl}`);
      
      // 检查用户是否登录
      const isLoggedIn = this.checkUserLogin();
      const needAuth = ['pages/ProfilePage', 'pages/SettingsPage'];
      
      if (needAuth.includes(to.pageUrl) && !isLoggedIn) {
        // 需要登录但用户未登录,跳转到登录页
        next({
          replace: true,
          pageUrl: 'pages/LoginPage',
          param: { redirectUrl: to.pageUrl }
        });
      } else {
        // 允许跳转
        next();
      }
    },
    afterEach: (to, from) => {
      console.log(`已从${from?.pageUrl}跳转到${to.pageUrl}`);
      // 可以在这里添加页面访问统计等逻辑
    }
  });
}

7. 实际应用案例

7.1 猜数字游戏的路由扩展

基于现有的猜数字游戏,我们可以扩展为多页面应用,演示路由功能:

1. 创建游戏设置页面
// pages/GameSettings.ets
import { HMRouter } from '@hadss/hmrouter';
import { PagePath } from '../constants/PagePath';

@HMRouter({ pageUrl: PagePath.GAME_SETTINGS })
@Entry
@Component
export struct GameSettings {
  @State minRange: number = 1;
  @State maxRange: number = 100;
  @State attemptsLimit: number = 10;

  onConfirm() {
    // 保存设置并返回
    AppStorage.SetOrCreate('gameSettings', {
      minRange: this.minRange,
      maxRange: this.maxRange,
      attemptsLimit: this.attemptsLimit
    });
    
    // 返回上一页
    import { HMRouterMgr } from '@hadss/hmrouter';
    HMRouterMgr.pop();
  }

  build() {
    Column() {
      Text('游戏设置')
        .fontSize(24)
        .margin({ bottom: 30 })
      
      // 设置项...
      
      Button('确定')
        .onClick(() => this.onConfirm())
    }
  }
}
2. 修改主游戏页面,添加设置按钮
// 修改Index.ets,添加设置功能
@HMRouter({ pageUrl: PagePath.INDEX })
@Entry
@Component
export struct GuessNumberGame {
  // 原有代码...
  
  // 从AppStorage读取设置
  private settings: any = AppStorage.Get('gameSettings') || { minRange: 1, maxRange: 100 };

  onSettingsClick() {
    import { HMRouterMgr } from '@hadss/hmrouter';
    import { PagePath } from '../constants/PagePath';
    
    HMRouterMgr.push({
      pageUrl: PagePath.GAME_SETTINGS
    });
  }

  build() {
    Column() {
      // 原有UI...
      
      // 添加设置按钮
      Row() {
        Button('设置')
          .onClick(() => this.onSettingsClick())
          .margin({ top: 20 })
      }
    }
  }
}

7.2 家族族谱应用的路由管理

本项目中的家族族谱应用展示了HMRouter在实际项目中的应用:

页面结构
// constants/PagePath.ets
export class PagePath {
  static readonly FAMILY_TREE: string = "FamilyTree"
  static readonly ADD_MEMBER: string = "AddMember"
  static readonly EDIT_MEMBER: string = "EditMember"
}
主页面导航
// pages/FamilyTree.ets
@HMRouter({ pageUrl: PagePath.FAMILY_TREE })
@ComponentV2
export struct FamilyTreeView {
  build() {
    Column() {
      // 页面内容...
      
      // 导航到添加成员页面
      Button('添加成员')
        .onClick(() => {
          HMRouterMgr.push({
            pageUrl: PagePath.ADD_MEMBER
          })
        })
    }
  }
}
数据传递与状态管理
// 使用@Local装饰器管理组件本地状态
@HMRouter({ pageUrl: PagePath.ADD_MEMBER })
@ComponentV2
export struct AddMember {
  @Local name: string = ''
  @Local gender: 'male' | 'female' = 'male'
  @Local birthDate: string = ''
  
  build() {
    // 表单内容...
  }
}

8. 最佳实践与注意事项

8.1 路由管理最佳实践

  1. 统一管理页面路径:使用常量类统一管理所有页面路径,避免硬编码

    // constants/PagePath.ets
    export class PagePath {
      static readonly INDEX = 'pages/Index';
      static readonly DETAIL = 'pages/DetailPage';
      static readonly SETTINGS = 'pages/SettingsPage';
    }
    
  2. 避免深层嵌套导航:设计合理的页面层级,避免过深的导航嵌套,影响用户体验

  3. 使用导航守卫处理通用逻辑:将登录检查、权限验证等通用逻辑放在导航守卫中

  4. 合理使用不同的跳转模式:根据场景选择push、replace或replaceAll

8.2 状态管理最佳实践

  1. 选择合适的装饰器

    • 组件内部状态使用@State
    • 父子组件通信使用@Prop和@Link
    • 跨组件状态共享使用@Provide/@Consume
    • 全局状态使用@AppStorage
    • 持久化状态使用@LocalStorage
  2. 合理使用@Local和@ComponentV2

    @ComponentV2
    export struct MyComponent {
      @Local myState: string = 'initial value'
      
      build() {
        // 使用myState
      }
    }
    
  3. 避免过度使用全局状态:只将真正需要全局共享的数据放入@AppStorage

8.3 常见问题与解决方案

  1. 路由参数传递失败

    • 检查参数格式是否正确
    • 确保目标页面正确接收参数
    • 对于复杂对象,考虑使用序列化/反序列化
  2. 页面跳转后状态丢失

    • 使用AppStorage或LocalStorage保存全局状态
    • 在onPageShow生命周期中恢复页面状态
  3. 动画效果不符合预期

    • 检查动画配置参数
    • 避免在动画期间执行重计算操作
    • 考虑使用默认动画配置进行调试
  4. 内存管理问题

    • 在onDestroy生命周期中清理资源
    • 避免在页面中持有过多的全局引用
    • 对于长时间运行的应用,考虑定期清理导航栈

9. 总结

页面路由和导航是ArkTS应用开发的重要组成部分。通过掌握原生Router模块和HMRouter等第三方路由框架的使用,可以构建出用户体验良好的多页面应用。在实际开发中,应根据应用的复杂度和需求选择合适的路由方案,并遵循路由管理的最佳实践,确保应用的性能和稳定性。

通过本课程的学习,您应该能够熟练掌握ArkTS中的页面路由和导航技术,包括基本的页面跳转、参数传递、导航动画和路由守卫等功能,为开发复杂的HarmonyOS应用打下坚实的基础。同时,我们也介绍了ArkTS中的状态管理机制,帮助您更好地处理页面间的数据传递和状态同步。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值