Harmony学习之页面路由与导航

Harmony学习之页面路由与导航

一、场景引入

小明在上一篇文章中完成了登录界面的开发,现在他需要实现从登录页跳转到首页,并在跳转时传递用户信息。同时,他还需要处理页面返回、参数接收等场景。本篇文章将系统讲解HarmonyOS的页面路由与导航机制,帮助小明实现多页面应用的完整跳转流程。

二、路由模块导入与配置

1. 导入路由模块

在需要使用路由功能的页面中,首先需要导入router模块:

import router from '@ohos.router';

2. 页面注册配置

resources/base/profile/main_pages.json文件中配置页面路径,确保所有需要跳转的页面都已注册:

{
  "src": [
    "pages/Index",
    "pages/Login",
    "pages/Home",
    "pages/Detail"
  ]
}

三、页面跳转方式

1. pushUrl - 压栈跳转

pushUrl方法会将目标页面压入页面栈,保留当前页面状态,可以通过返回键或router.back()返回到当前页:

// 基本跳转
router.pushUrl({
  url: 'pages/Home'
});

// 带参数跳转
router.pushUrl({
  url: 'pages/Detail',
  params: {
    id: 123,
    name: '商品详情'
  }
});

2. replaceUrl - 替换跳转

replaceUrl方法会用目标页面替换当前页面,销毁当前页面资源,无法返回原页面:

// 登录成功后跳转到首页,销毁登录页
router.replaceUrl({
  url: 'pages/Home'
});

3. 跳转模式

HarmonyOS提供两种跳转模式,通过router.RouterMode指定:

// Standard模式(默认):每次跳转都创建新实例
router.pushUrl({
  url: 'pages/Detail',
  params: { id: 123 }
}, router.RouterMode.Standard);

// Single模式:如果目标页已存在,则移动到栈顶
router.pushUrl({
  url: 'pages/Detail',
  params: { id: 456 }
}, router.RouterMode.Single);

跳转模式对比

模式行为适用场景
Standard每次跳转都创建新实例商品详情页、新闻详情页
Single复用已存在页面,移动到栈顶设置页、个人中心页

四、参数传递与接收

1. 发送参数

在跳转时通过params属性传递参数:

// 登录页跳转到首页,传递用户信息
router.pushUrl({
  url: 'pages/Home',
  params: {
    userId: '123456',
    userName: '小明',
    token: 'abcdefg'
  }
});

2. 接收参数

在目标页面的生命周期方法中接收参数:

@Entry
@Component
struct Home {
  @State userId: string = '';
  @State userName: string = '';

  // 推荐在aboutToAppear中接收参数
  aboutToAppear() {
    const params = router.getParams();
    this.userId = params?.['userId'] || '';
    this.userName = params?.['userName'] || '';
  }

  build() {
    Column() {
      Text(`欢迎回来,${this.userName}`)
        .fontSize(20)
        .margin({ bottom: 20 })
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
  }
}

3. 参数类型定义

建议定义参数类型接口,提高代码可读性和类型安全:

// 定义参数类型
interface UserInfo {
  userId: string;
  userName: string;
  token: string;
}

// 发送参数
router.pushUrl({
  url: 'pages/Home',
  params: {
    userId: '123456',
    userName: '小明',
    token: 'abcdefg'
  } as UserInfo
});

// 接收参数
aboutToAppear() {
  const params = router.getParams() as UserInfo;
  this.userId = params.userId;
  this.userName = params.userName;
}

五、页面返回与结果传递

1. 返回上一页

// 简单返回
router.back();

// 返回并传递结果
router.back({
  result: {
    status: 'success',
    message: '操作成功'
  }
});

2. 返回到指定页面

// 返回到指定页面
router.back({
  url: 'pages/Home'
});

// 返回到指定页面并传递参数
router.back({
  url: 'pages/Home',
  params: {
    refresh: true
  }
});

3. 接收返回结果

在目标页面的onPageShow生命周期中接收返回结果:

@Entry
@Component
struct Home {
  onPageShow() {
    const result = router.getParams();
    if (result?.refresh) {
      // 刷新数据
      this.loadData();
    }
  }
}

六、页面生命周期

1. 页面生命周期方法

@Entry装饰的组件拥有完整的页面生命周期:

@Entry
@Component
struct DetailPage {
  // 页面即将显示时触发
  aboutToAppear() {
    console.log('页面即将显示');
  }

  // 页面每次显示时触发
  onPageShow() {
    console.log('页面显示');
    // 适合刷新数据
    this.refreshData();
  }

  // 页面每次隐藏时触发
  onPageHide() {
    console.log('页面隐藏');
    // 适合保存状态
    this.saveState();
  }

  // 用户点击返回按钮时触发
  onBackPress() {
    console.log('返回按钮被点击');
    // 返回true可拦截默认返回行为
    return false;
  }

  // 页面即将销毁时触发
  aboutToDisappear() {
    console.log('页面即将销毁');
    // 清理资源
    this.cleanup();
  }
}

2. 组件生命周期方法

普通组件(@Component装饰)的生命周期:

@Component
struct UserCard {
  // 组件即将显示时触发
  aboutToAppear() {
    console.log('组件即将显示');
  }

  // 组件即将销毁时触发
  aboutToDisappear() {
    console.log('组件即将销毁');
  }

  // API 12新增:组件build完成后触发
  onDidBuild() {
    console.log('组件构建完成');
    // 不建议在此修改状态变量
  }
}

七、页面栈管理

1. 获取页面栈信息

// 获取页面栈长度
const length = router.getLength();
console.log(`当前页面栈长度:${length}`);

// 获取当前页面状态
const state = router.getState();
console.log('当前页面状态:', state);

2. 清空页面栈

页面栈最大容量为32个页面,超出时需要清理:

// 清空页面栈
router.clear();

八、实战案例:登录跳转完整流程

1. 登录页面(Login.ets)

import router from '@ohos.router';

@Entry
@Component
struct Login {
  @State username: string = '';
  @State password: string = '';

  // 模拟登录成功
  private loginSuccess() {
    // 跳转到首页,传递用户信息
    router.replaceUrl({
      url: 'pages/Home',
      params: {
        userId: '123456',
        userName: this.username,
        token: 'login_token_123'
      }
    });
  }

  build() {
    Column({ space: 20 }) {
      TextInput({ placeholder: '请输入用户名' })
        .width('90%')
        .height(50)
        .onChange((value: string) => {
          this.username = value;
        })

      TextInput({ placeholder: '请输入密码' })
        .width('90%')
        .height(50)
        .type(InputType.Password)
        .onChange((value: string) => {
          this.password = value;
        })

      Button('登录')
        .width('90%')
        .height(50)
        .onClick(() => {
          this.loginSuccess();
        })
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
  }
}

2. 首页(Home.ets)

import router from '@ohos.router';

interface UserInfo {
  userId: string;
  userName: string;
  token: string;
}

@Entry
@Component
struct Home {
  @State userId: string = '';
  @State userName: string = '';
  @State token: string = '';

  aboutToAppear() {
    // 接收登录页传递的参数
    const params = router.getParams() as UserInfo;
    this.userId = params.userId;
    this.userName = params.userName;
    this.token = params.token;

    console.log('用户信息:', params);
  }

  build() {
    Column({ space: 20 }) {
      Text(`欢迎回来,${this.userName}`)
        .fontSize(24)
        .fontWeight(FontWeight.Bold)

      Button('查看详情')
        .width(200)
        .onClick(() => {
          router.pushUrl({
            url: 'pages/Detail',
            params: {
              id: 1001,
              title: '商品详情'
            }
          });
        })

      Button('退出登录')
        .width(200)
        .backgroundColor(Color.Red)
        .onClick(() => {
          router.replaceUrl({
            url: 'pages/Login'
          });
        })
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
  }
}

3. 详情页(Detail.ets)

import router from '@ohos.router';

interface DetailParams {
  id: number;
  title: string;
}

@Entry
@Component
struct Detail {
  @State id: number = 0;
  @State title: string = '';

  aboutToAppear() {
    const params = router.getParams() as DetailParams;
    this.id = params.id;
    this.title = params.title;

    console.log('详情页参数:', params);
  }

  build() {
    Column({ space: 20 }) {
      Text(`商品ID:${this.id}`)
        .fontSize(18)

      Text(`商品标题:${this.title}`)
        .fontSize(18)

      Button('返回')
        .width(200)
        .onClick(() => {
          router.back({
            result: {
              refresh: true
            }
          });
        })
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
  }
}

九、最佳实践与注意事项

1. 参数传递优化

推荐做法

  • 传递最小必要数据(如ID),在目标页面重新查询完整数据
  • 使用类型接口定义参数,提高代码可读性
  • 避免传递大对象或函数

不推荐做法

// 不推荐:传递大对象
router.pushUrl({
  url: 'pages/Detail',
  params: {
    // 大对象数据...
  }
});

2. 生命周期使用建议

生命周期方法推荐使用场景注意事项
aboutToAppear组件初始化、数据加载适合首次加载数据
onPageShow页面显示时刷新数据适合从后台返回时刷新
onPageHide保存页面状态、释放资源避免耗时操作
aboutToDisappear清理定时器、取消订阅禁止修改状态变量

3. 页面栈管理

  • 页面栈最大容量为32个页面,超出时需要调用router.clear()清理
  • 使用Single模式避免重复创建相同页面
  • 及时清理不再需要的页面,释放内存

4. 错误处理

router.pushUrl({
  url: 'pages/Detail',
  params: { id: 123 }
}, (err) => {
  if (err) {
    console.error('跳转失败:', err);
    // 处理错误,如跳转失败提示
    return;
  }
  console.log('跳转成功');
});

十、总结与行动建议

核心要点回顾

  1. 路由模块导入import router from '@ohos.router'
  2. 跳转方式pushUrl(压栈)、replaceUrl(替换)
  3. 参数传递:通过params传递,在目标页面router.getParams()接收
  4. 页面返回router.back()返回上一页,可传递返回结果
  5. 生命周期aboutToAppearonPageShowonPageHide等生命周期方法
  6. 页面栈管理:最大32个页面,使用router.clear()清理

行动建议

  1. 动手实践:按照本文示例,完成登录-首页-详情页的完整跳转流程
  2. 参数传递练习:尝试传递不同类型的数据(字符串、数字、对象)
  3. 生命周期调试:在生命周期方法中添加日志,观察调用顺序
  4. 页面栈管理:创建多个页面,测试页面栈的压栈和出栈行为
  5. 错误处理:添加跳转失败的错误处理逻辑

通过本篇文章的学习,你已经掌握了HarmonyOS页面路由与导航的核心能力。下一篇文章将深入讲解网络请求与数据获取,帮助你实现从服务器获取数据并展示在界面上。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值