鸿蒙技术干货2:鸿蒙页面路由与状态管理,从单页到多页应用

63 阅读3分钟

一、鸿蒙页面路由核心概念

1. 路由基础与配置

鸿蒙通过@ohos/router模块实现页面跳转,核心前提是在main_pages.json中注册页面路径(所有跳转页面必须配置):

// main_pages.json{  "src": [    "pages/TaskListPage", // 任务列表页(首页)    "pages/TaskDetailPage" // 任务详情页  ]}

路由核心 API 说明:

API作用关键参数
router.pushUrl()打开新页面(入栈)url(页面路径)、params(传递参数)
router.backUrl()返回上一页(出栈)params(返回时携带参数)
router.clear()清空路由栈-

2. 页面参数传递

正向传递(列表页→详情页):通过pushUrl的params携带数据(支持 string/number/boolean/object 类型)

反向传递(详情页→列表页):通过backUrl的params携带返回数据,列表页通过onPageShow接收

二、跨组件状态管理:@Link 与 @Provide/@Consume

单页应用中@State足够使用,但多页 / 跨组件场景需以下装饰器:

1. @Link:父子组件双向绑定

作用:实现父子组件数据同步(修改子组件数据会同步到父组件,反之亦然)

使用场景:详情页修改任务状态,同步到列表页(父子页面关系)

语法:父组件传递时加$,子组件用@Link接收

2. @Provide/@Consume:跨层级数据共享

作用:解决多层嵌套组件的数据传递(无需层层传递参数)

使用场景:全局任务列表数据共享(列表页→详情页→子组件)

语法:@Provide(提供方,如列表页)声明数据,@Consume(消费方,如详情页子组件)接收,二者通过相同的「别名」关联

三、实战:开发任务清单 APP(完整代码)

1. 需求说明

列表页(TaskListPage):展示所有任务,点击任务进入详情页,支持新增任务

详情页(TaskDetailPage):编辑任务标题、切换完成状态,返回后列表页同步更新

状态同步:详情页修改任务后,列表页实时刷新

2. 步骤 1:定义任务数据模型

新建model/TaskModel.ets,统一数据结构:

// model/TaskModel.etsexport interface Task {  id: string; // 任务唯一ID  title: string; // 任务标题  isCompleted: boolean; // 完成状态}

3. 步骤 2:任务列表页(TaskListPage)

作为路由首页和数据提供方,使用@Provide共享任务列表:

// pages/TaskListPage.etsimport router from '@ohos/router';import { Task } from '../model/TaskModel';import { FlexAlign, ItemAlign } from '@ohos/ui';@Componentexport struct TaskListPage {  // 提供全局任务数据(供详情页消费)  @Provide('taskList') taskList: Task[] = [    { id: '1', title: '学习鸿蒙路由', isCompleted: false },    { id: '2', title: '掌握@Link状态绑定', isCompleted: true }  ];  // 接收详情页返回的参数  onPageShow() {    const params = router.getParams();    if (params?.updatedTask) {      // 详情页修改后,更新列表数据      const updatedTask = params.updatedTask as Task;      this.taskList = this.taskList.map(task =>        task.id === updatedTask.id ? updatedTask : task      );    }  }  // 新增任务(简化逻辑)  addTask() {    this.taskList.push({      id: Date.now().toString(),      title: 新任务${this.taskList.length + 1},      isCompleted: false    });  }  build() {    Column({ space: 20 })      .width('100%')      .height('100%')      .padding(30)      .backgroundColor('#f5f5f5') {      // 标题与新增按钮      Row({ justifyContent: FlexAlign.SpaceBetween, alignItems: ItemAlign.Center }) {        Text('任务清单')          .fontSize(32)          .fontWeight(FontWeight.Bold)        Button('新增任务')          .type(ButtonType.Capsule)          .backgroundColor('#4a6cf7')          .onClick(() => this.addTask())      }      // 任务列表      List({ space: 15 }) {        ForEach(this.taskList, (task: Task) => {          ListItem() {            Row({              justifyContent: FlexAlign.SpaceBetween,              alignItems: ItemAlign.Center            })              .width('100%')              .padding(20)              .backgroundColor('#ffffff')              .borderRadius(12)              .onClick(() => {                // 跳转到详情页,传递当前任务(正向参数)                router.pushUrl({                  url: 'pages/TaskDetailPage',                  params: { task: task }                });              }) {              // 任务标题(完成状态划横线)              Text(task.title)                .fontSize(22)                .fontColor(task.isCompleted ? '#999999' : '#333333')                .textDecoration(task.isCompleted ? TextDecorationType.LineThrough : TextDecorationType.None)              // 完成状态图标              Image(task.isCompleted                ? r(app.media.check)                :r('app.media.check')                : r('app.media.uncheck')              )                .width(30)                .height(30)            }          }        })      }    }  }}

4. 步骤 3:任务详情页(TaskDetailPage)

接收列表页参数,使用@Consume获取全局任务,@Link绑定当前任务:

// pages/TaskDetailPage.etsimport router from '@ohos/router';import { Task } from '../model/TaskModel';import { FlexAlign, ItemAlign } from '@ohos/ui';@Componentexport struct TaskDetailPage {  // 接收列表页传递的任务参数  private task: Task = router.getParams().task as Task;  // 双向绑定任务(修改后同步到列表页)  @Link currentTask: Task = this.task;  // 消费全局任务列表(用于获取最新数据)  @Consume('taskList') taskList: Task[];  build() {    Column({ space: 40 })      .width('100%')      .height('100%')      .padding(30)      .backgroundColor('#f5f5f5') {      // 任务标题输入框      TextInput({        placeholder: '输入任务标题',        text: this.currentTask.title      })        .width('100%')        .height(60)        .padding(20)        .backgroundColor('#ffffff')        .borderRadius(12)        .fontSize(22)        .onChange((value: string) => {          // 修改标题,双向绑定同步          this.currentTask.title = value;        })      // 完成状态切换      Row({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) {        Text('标记为已完成')          .fontSize(22)          .fontColor('#333333')        Switch()          .checked(this.currentTask.isCompleted)          .onChange((isChecked: boolean) => {            // 切换状态,双向绑定同步            this.currentTask.isCompleted = isChecked;          })      }      // 返回按钮(携带修改后的任务返回列表页)      Button('保存并返回')        .type(ButtonType.Capsule)        .width(250)        .height(60)        .fontSize(22)        .backgroundColor('#4a6cf7')        .onClick(() => {          // 反向传递参数,通知列表页更新          router.backUrl({            params: { updatedTask: this.currentTask }          });        })    }  }}

5. 关键代码说明

(1)路由跳转与参数传递

列表页→详情页:router.pushUrl传递task对象,详情页通过router.getParams()接收

详情页→列表页:router.backUrl传递updatedTask,列表页在onPageShow中接收并更新列表

(2)状态同步核心

@Provide('taskList'):列表页提供全局任务数据,详情页通过@Consume('taskList')接收,确保数据一致性

@Link currentTask:详情页与列表页的任务对象双向绑定,修改currentTask会直接同步到列表页的taskList

四、常见问题与解决方案

1. 页面跳转失败

检查main_pages.json是否注册目标页面路径

确保pushUrl的url路径与配置一致(如pages/TaskDetailPage,不带.ets后缀)

2. 状态不同步

使用@Link时,父组件传递必须加(如currentTask=(如currentTask=task)

复杂对象修改后需触发引用更新,可使用扩展运算符:this.currentTask = {...this.currentTask, title: value}

3. 参数传递类型限制

不支持传递函数、Promise 等复杂类型,建议仅传递 JSON 可序列化的数据(字符串、数字、对象、数组)

加入班级,一起学习鸿蒙开发