前言

学习页面路由不用一次性啃完整业务代码,本文按照简易→进阶→完整业务的梯度分层讲解,从只实现跳转的极简页面,逐步增加传参、表单、校验逻辑,循序渐进掌握@ohos.router路由 API,新手更容易理解吸收。

前置统一配置(所有阶段都需要)

所有页面必须在main_pages.json注册,否则路由找不到页面,固定配置如下:

{
  "src": [
    "pages/Index",
    "pages/second",
    "pages/RouterDemo",
    "pages/Resister",
    "pages/LoginPage",
    "pages/RouterLogin",
    "pages/RouterRegister"

  ]
}

第一阶段:最简路由(仅页面跳转,无表单、无传参)

1.1 入口页面 RouterDemo.ets(路由总入口)

功能:只有两个文字,点击分别跳转到简易注册、简易登录,只用到最基础router.pushUrl

import router from '@ohos.router';
@Entry
@Component
struct RouterDemo{
  build(){
    Column({space:20}){
      //跳转到注册页面
      Text('没有账号,立即注册')
        .fontSize(22)
        .fontColor(0x1677ff)
        .onClick(()=>{
          router.pushUrl({
            url:"pages/Resister"
          })
        })
      //跳转到登陆页面
      Text('已有账号,立即登录')
        .fontSize(22)
        .fontColor(0x1655dd)
        .onClick(()=>{
          router.pushUrl({
            url:"pages/LoginPage"
          })
        })
    }.width('100%')
    .height('100%')
    .padding(12)
  }
}

1.2 极简注册页 Resister.ets(无任何交互)

仅展示文字,无返回、无输入框,用来验证跳转是否成功

@Entry
@Component
struct Resister{
  build() {
    Column(){
      Text("你好,这是一个注册页面!")
        .fontSize(25)
        .fontColor(0x77d441)

    }
    .width("100%")
    .height("100%")
  }
}

阶段 1 核心知识点

  1. import router from '@ohos.router':所有路由操作必须导入;
  2. router.pushUrl({url:"页面路径"}):打开新页面,页面入栈,系统返回键可以回到上一页;
  3. url 路径必须和main_pages.json注册名称完全一致,区分大小写。

第二阶段:进阶路由(增加基础表单、页面互相跳转)

在阶段 1 基础上,给登录页面增加账号密码输入框,同时增加页面互跳逻辑(登录页可以点击文字跳注册页)

2.1 LoginPage.ets 简易登录表单页

新增 Image 头像、账号 / 密码输入框、页面互跳文字按钮

import router from '@ohos.router';
@Entry
@Component
struct RouterLogin{
  build() {
    Column({space:25}){
// 顶部头像图片
      Image($r('app.media.图片名称'))
        .width(120)
        .height(120)
        .borderRadius(60)

      Text("登 录")
        .fontSize(32)
        .fontWeight(FontWeight.Bold)

      Row({space:15}){
        Text("账 号")
          .fontSize(26)
        TextInput()
          .width("70%")
          .height(50)
      }
      Row({space:15}){
        Text("密 码")
          .fontSize(26)
        TextInput()
          .width("70%")
          .height(50)
          .type(InputType.Password)
      }
      Text("没有账号,立即注册")
        .fontSize(22)
        .fontColor(0xababab)
        .onClick(()=>{
          router.pushUrl({
            url:"pages/RouterRegister"
          })
        })
      Button("立 即 登 录")
        .width('100%')
        .height(50)
        .fontSize(24)
    }
    .width('100%')
    .height('100%')
    .padding(15)
  }
}

2.2 RouterLogin.ets 标准登录页(纯 UI,无逻辑)

和 LoginPage 结构一致,作为正式登录页面模板,仅 UI 展示,暂不做登录业务逻辑

import router from '@ohos.router';
@Entry
@Component
struct RouterRegister{
  build() {
    Column({space:25}){
      Image($r('app.media.banner0'))
        .width(120)
        .height(120)
        .borderRadius(60)

      Text("登录")
        .fontSize(32)
        .fontWeight(FontWeight.Bolder)
      Row(){
        Text("账号")
          .fontSize(26)
          .width('40%')
          .textAlign(TextAlign.Center)
        TextInput()
          .width('60%')
          .height(50)


      }
      Row(){
        Text("密码")
          .fontSize(26)
          .width('40%')
          .textAlign(TextAlign.Center)

        TextInput()
          .width('60%')
          .height(50)
          .type(InputType.Password)

      }

      Text("没有账号,立即注册")
        .fontSize(22)
        .fontColor(0xababab)
        .onClick(()=>{
          router.pushUrl({
            url:"pages/RouterRegister"
          })
        })
      Button("登录")
        .width('100%')
        .height(50)
        .fontSize(24)

    }
    .width('100%')
    .height('100%')
    .padding(15)
  }

}

阶段 2 新增知识点

  1. TextInput(type:InputType.Password):密码框隐藏输入内容;
  2. 多页面循环跳转:登录页 ↔ 注册页互相导航,熟悉页面栈叠加逻辑;
  3. Image 资源使用:提前在 media 文件夹放置对应图片资源。

第三阶段:中级路由(双向数据绑定 + 表单校验)

新增完整注册页面RouterRegister.ets,使用@State实现输入框双向绑定,增加表单校验、弹窗提示

import router from '@ohos.router';
@Entry
@Component
struct RouterRegister{
  @State username:string = ""
  @State password:string = ""
  @State password2:string = ""

  build() {
    Column({space:25}){
      Image($r('app.media.banner0'))
        .width(120)
        .height(120)
        .borderRadius(60)

      Text("注册")
        .fontSize(32)
        .fontWeight(FontWeight.Bolder)
      Row(){
        Text("账号")
          .fontSize(26)
          .width('40%')
          .textAlign(TextAlign.Center)
        TextInput({text:this.username})
          .width('60%')
          .height(50)
          .onChange((value:string)=> {
            this.username = value
          })

      }
      Row(){
        Text("密码")
          .fontSize(26)
          .width('40%')
          .textAlign(TextAlign.Center)

        TextInput({text:this.password})
          .width('60%')
          .height(50)
          .type(InputType.Password)
          .onChange((value:string)=> {
            this.password = value
          })

      }

      Row(){
        Text("确认密码")
          .fontSize(26)
          .width('40%')
          .textAlign(TextAlign.Center)

        TextInput({text:this.password2})
          .width('60%')
          .height(50)
          .type(InputType.Password)
          .onChange((value:string)=> {
            this.password2 = value
          })

      }

      Text("已有账号,立即登录")
        .fontSize(22)
        .fontColor(0xababab)
        .onClick(()=>{
          router.pushUrl({
            url:"pages/RouterLogin"
          })
        })
      Button("立即注册")
        .width('100%')
        .height(50)
        .fontSize(24)
        .onClick(()=>{
          if ((this.username!="") && (this.password!="") && (this.password2!="") &&(this.password==this.password2)){
            router.pushUrl({
              url:"pages/RouterLogin",
              params:{
                username:this.username,
                password:this.password
              }
            })
          }
          else {
            AlertDialog.show({
              title:"注册失败",
              message:`注册的两次密码为空或者不一致,${this.username}`
            })
          }
        })
    }
    .width('100%')
    .height('100%')
    .padding(15)
  }

}

阶段 3 新增核心知识点

  1. @State装饰器:实现 UI 输入框与变量双向绑定;
  2. onChange监听输入框变化,实时保存用户输入;
  3. params路由传参:跳转页面时携带自定义数据对象;
  4. AlertDialog.show弹窗提示,表单校验失败反馈;
  5. 基础逻辑判断if-else完成表单合规校验。

第四阶段:高阶路由(接收页面传递参数,完整业务闭环)

新增HomePage.ets用户主页,使用router.getParams()接收上一页传递的用户名,完成注册→登录→主页完整业务链路

import router from '@ohos.router';
@Entry
@Component
struct HomePage{
  @State username:string = ""
  onPageShow(): void {
    const params = router.getParams() as Record<string,string>
    if (params){
      this.username = params.username
    }
  }
  build() {
    Column(){
      Text('你好${this.username}')
    }
  }
}

阶段 4 新增知识点

  1. 页面生命周期onPageShow:页面显示时执行,适合接收路由参数;
  2. router.getParams():获取上一个页面跳转时传递的 params 数据;
  3. 类型断言as Record<string,string>解决 TS 类型警告;
  4. 完整业务闭环:注册填写信息 → 传参到登录页 → 登录跳转主页展示用户名称。

整体学习顺序 & 运行流程

  1. 先运行阶段 1:掌握最基础页面跳转,确认路由配置无报错;
  2. 再运行阶段 2:学会表单 UI、页面互相跳转;
  3. 接着阶段 3:学习双向绑定、表单校验、路由传参;
  4. 最后阶段 4:掌握参数接收,打通完整登录注册业务。

开发避坑总结(按学习阶段对应)

  1. 阶段 1 踩坑:页面找不到 检查main_pages.json是否注册页面,url 路径大小写、拼写必须完全匹配。
  2. 阶段 2 踩坑:图片资源报错 media 文件夹必须存在同名图片banner0,资源名称不能包含中文、空格。
  3. 阶段 3 踩坑:输入框内容不更新 必须给TextInput绑定text参数 +onChange同步 @State 变量,缺一不可。
  4. 阶段 4 踩坑:接收不到 params 参数 获取参数代码写在onPageShow/aboutToAppear生命周期,不要写在 build 渲染函数中,同时增加 params 非空判断。

拓展优化建议(学完全部阶段后练习)

  1. 登录按钮增加跳转主页逻辑,使用router.replaceUrl替换页面,避免页面栈堆积;
  2. 封装路由工具类,统一管理页面路径常量,消除硬编码 url;
  3. 增加路由拦截:未登录时强制跳登录页;
  4. 完善表单校验:账号密码长度限制、特殊字符过滤;
  5. 登录成功添加 Toast 弹窗提示,优化用户交互体验。

Logo

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

更多推荐