1# 页面路由 (@ohos.router)(不推荐)
2
3
4页面路由指在应用程序中实现不同页面之间的跳转和数据传递。Router模块通过不同的url地址,可以方便地进行页面路由,轻松地访问不同的页面。本文将从[页面跳转](#页面跳转)、[页面返回](#页面返回)、[页面返回前增加一个询问框](#页面返回前增加一个询问框)和[命名路由](#命名路由)这几个方面,介绍如何通过Router模块实现页面路由。
5
6>**说明:**
7>
8>[组件导航 (Navigation)](./arkts-navigation-navigation.md)具有更强的功能和自定义能力,推荐使用该组件作为应用的路由框架。Navigation和Router的差异可参考[Router切换Navigation](./arkts-router-to-navigation.md)指导。
9
10## 页面跳转
11
12页面跳转是开发过程中的一个重要组成部分。在使用应用程序时,通常需要在不同的页面之间跳转,有时还需要将数据从一个页面传递到另一个页面。
13
14  **图1** 页面跳转
15![router-jump-to-detail](figures/router-jump-to-detail.gif)
16
17Router模块提供了两种跳转模式,分别是[router.pushUrl](../reference/apis-arkui/js-apis-router.md#routerpushurl9)和[router.replaceUrl](../reference/apis-arkui/js-apis-router.md#routerreplaceurl9)。这两种模式决定了目标页面是否会替换当前页。
18
19- router.pushUrl:目标页面不会替换当前页,而是压入页面栈。这样可以保留当前页的状态,并且可以通过返回键或者调用[router.back](../reference/apis-arkui/js-apis-router.md#routerback)方法返回到当前页。
20
21- router.replaceUrl:目标页面会替换当前页,并销毁当前页。这样可以释放当前页的资源,并且无法返回到当前页。
22
23>**说明:**
24>
25>- 创建新页面时,请参考<!--RP1-->[构建第二个页面](../quick-start/start-with-ets-stage.md#构建第二个页面)<!--RP1End-->配置第二个页面的路由。
26>
27>
28>- 页面栈的最大容量为32个页面。如果超过这个限制,可以调用[router.clear](../reference/apis-arkui/js-apis-router.md#routerclear)方法清空历史页面栈,释放内存空间。
29
30同时,Router模块提供了两种实例模式,分别是Standard和Single。这两种模式决定了目标url是否会对应多个实例。
31
32- Standard:多实例模式,也是默认情况下的跳转模式。目标页面会被添加到页面栈顶,无论栈中是否存在相同url的页面。
33
34- Single:单实例模式。如果目标页面的url已经存在于页面栈中,则会将离栈顶最近的同url页面移动到栈顶,该页面成为新建页。如果目标页面的url在页面栈中不存在同url页面,则按照默认的多实例模式进行跳转。
35
36在使用Router相关功能之前,需要在代码中先导入Router模块。
37
38
39```ts
40import { promptAction, router } from '@kit.ArkUI';
41import { BusinessError } from '@kit.BasicServicesKit';
42```
43
44- 场景一:有一个主页(Home)和一个详情页(Detail),希望从主页点击一个商品,跳转到详情页。同时,需要保留主页在页面栈中,以便返回时恢复状态。这种场景下,可以使用pushUrl方法,并且使用Standard实例模式(或者省略)。
45
46  ```ts
47  import { router } from '@kit.ArkUI';
48
49  // 在Home页面中
50  function onJumpClick(): void {
51    router.pushUrl({
52      url: 'pages/Detail' // 目标url
53    }, router.RouterMode.Standard, (err) => {
54      if (err) {
55        console.error(`Invoke pushUrl failed, code is ${err.code}, message is ${err.message}`);
56        return;
57      }
58      console.info('Invoke pushUrl succeeded.');
59    });
60  }
61  ```
62
63  >**说明:**
64  >
65  >多实例模式下,router.RouterMode.Standard参数可以省略。
66
67- 场景二:有一个登录页(Login)和一个个人中心页(Profile),希望从登录页成功登录后,跳转到个人中心页。同时,销毁登录页,在返回时直接退出应用。这种场景下,可以使用replaceUrl方法,并且使用Standard实例模式(或者省略)。
68
69  ```ts
70  import { router } from '@kit.ArkUI';
71
72  // 在Login页面中
73  function onJumpClick(): void {
74    router.replaceUrl({
75      url: 'pages/Profile' // 目标url
76    }, router.RouterMode.Standard, (err) => {
77      if (err) {
78        console.error(`Invoke replaceUrl failed, code is ${err.code}, message is ${err.message}`);
79        return;
80      }
81      console.info('Invoke replaceUrl succeeded.');
82    })
83  }
84  ```
85
86  >**说明:**
87  >
88  >多实例模式下,router.RouterMode.Standard参数可以省略。
89
90- 场景三:有一个设置页(Setting)和一个主题切换页(Theme),希望从设置页点击主题选项,跳转到主题切换页。同时,需要保证每次只有一个主题切换页存在于页面栈中,在返回时直接回到设置页。这种场景下,可以使用pushUrl方法,并且使用Single实例模式。
91
92  ```ts
93  import { router } from '@kit.ArkUI';
94
95  // 在Setting页面中
96  function onJumpClick(): void {
97    router.pushUrl({
98      url: 'pages/Theme' // 目标url
99    }, router.RouterMode.Single, (err) => {
100      if (err) {
101        console.error(`Invoke pushUrl failed, code is ${err.code}, message is ${err.message}`);
102        return;
103      }
104      console.info('Invoke pushUrl succeeded.');
105    });
106  }
107  ```
108
109- 场景四:有一个搜索结果列表页(SearchResult)和一个搜索结果详情页(SearchDetail),希望从搜索结果列表页点击某一项结果,跳转到搜索结果详情页。同时,如果该结果已经被查看过,则不需要再新建一个详情页,而是直接跳转到已经存在的详情页。这种场景下,可以使用replaceUrl方法,并且使用Single实例模式。
110
111  ```ts
112  import { router } from '@kit.ArkUI';
113
114  // 在SearchResult页面中
115  function onJumpClick(): void {
116    router.replaceUrl({
117      url: 'pages/SearchDetail' // 目标url
118    }, router.RouterMode.Single, (err) => {
119      if (err) {
120        console.error(`Invoke replaceUrl failed, code is ${err.code}, message is ${err.message}`);
121        return;
122      }
123      console.info('Invoke replaceUrl succeeded.');
124    })
125  }
126  ```
127
128以上是不带参数传递的场景。
129
130如果需要在跳转时传递一些数据给目标页面,则可以在调用Router模块的方法时,添加一个params属性,并指定一个对象作为参数。例如:
131
132
133```ts
134import { router } from '@kit.ArkUI';
135
136class DataModelInfo {
137  age: number = 0;
138}
139
140class DataModel {
141  id: number = 0;
142  info: DataModelInfo | null = null;
143}
144
145function onJumpClick(): void {
146  // 在Home页面中
147  let paramsInfo: DataModel = {
148    id: 123,
149    info: {
150      age: 20
151    }
152  };
153
154  router.pushUrl({
155    url: 'pages/Detail', // 目标url
156    params: paramsInfo // 添加params属性,传递自定义参数
157  }, (err) => {
158    if (err) {
159      console.error(`Invoke pushUrl failed, code is ${err.code}, message is ${err.message}`);
160      return;
161    }
162    console.info('Invoke pushUrl succeeded.');
163  })
164}
165```
166
167在目标页面中,可以通过调用Router模块的[getParams](../reference/apis-arkui/js-apis-router.md#routergetparams)方法来获取传递过来的参数。例如:
168
169
170```ts
171import { router } from '@kit.ArkUI';
172
173class InfoTmp {
174  age: number = 0
175}
176
177class RouTmp {
178  id: object = () => {
179  }
180  info: InfoTmp = new InfoTmp()
181}
182
183const params: RouTmp = router.getParams() as RouTmp; // 获取传递过来的参数对象
184const id: object = params.id // 获取id属性的值
185const age: number = params.info.age // 获取age属性的值
186```
187
188
189## 页面返回
190
191当用户在一个页面完成操作后,通常需要返回到上一个页面或者指定页面,这就需要用到页面返回功能。在返回的过程中,可能需要将数据传递给目标页面,这就需要用到数据传递功能。
192
193  **图2** 页面返回  
194
195![router-back-to-home](figures/router-back-to-home.gif)
196
197在使用页面路由Router相关功能之前,需要在代码中先导入Router模块。
198
199
200```ts
201import { router } from '@kit.ArkUI';
202```
203
204可以使用以下几种方式返回页面:
205
206- 方式一:返回到上一个页面。
207
208
209  ```ts
210  import { router } from '@kit.ArkUI';
211
212  router.back();
213  ```
214
215  这种方式会返回到上一个页面,即上一个页面在页面栈中的位置。但是,上一个页面必须存在于页面栈中才能够返回,否则该方法将无效。
216
217- 方式二:返回到指定页面。
218
219
220  返回普通页面。
221
222  ```ts
223  import { router } from '@kit.ArkUI';
224
225  router.back({
226    url: 'pages/Home'
227  });
228  ```
229
230  返回命名路由页面。
231
232  ```ts
233  import { router } from '@kit.ArkUI';
234
235  router.back({
236    url: 'myPage' //myPage为返回的命名路由页面别名
237  });
238  ```
239
240  这种方式可以返回到指定页面,需要指定目标页面的路径。目标页面必须存在于页面栈中才能够返回。
241
242- 方式三:返回到指定页面,并传递自定义参数信息。
243
244
245  返回到普通页面。
246
247  ```ts
248  import { router } from '@kit.ArkUI';
249
250  router.back({
251    url: 'pages/Home',
252    params: {
253      info: '来自Home页'
254    }
255  });
256  ```
257
258  返回命名路由页面。
259
260  ```ts
261  import { router } from '@kit.ArkUI';
262
263  router.back({
264    url: 'myPage', //myPage为返回的命名路由页面别名
265    params: {
266      info: '来自Home页'
267    }
268  });
269  ```
270
271  这种方式不仅可以返回到指定页面,还可以在返回的同时传递自定义参数信息。这些参数信息可以在目标页面中通过调用router.getParams方法进行获取和解析。
272
273在目标页面中,在需要获取参数的位置调用router.getParams方法即可,例如在[onPageShow](../reference/apis-arkui/arkui-ts/ts-custom-component-lifecycle.md#onpageshow)生命周期回调中:
274
275> **说明:**
276>
277> 直接使用router可能导致实例不明确的问题,建议使用[getUIContext](../reference/apis-arkui/js-apis-arkui-UIContext.md#uicontext)获取UIContext实例,并使用[getRouter](../reference/apis-arkui/js-apis-arkui-UIContext.md#getrouter)获取绑定实例的router。
278
279```ts
280@Entry
281@Component
282struct Home {
283  @State message: string = 'Hello World';
284
285  onPageShow() {
286    const params = this.getUIContext().getRouter().getParams() as Record<string, string>; // 获取传递过来的参数对象
287    if (params) {
288      const info: string = params.info as string; // 获取info属性的值
289    }
290  }
291
292  // ...
293}
294```
295
296>**说明:**
297>
298>当使用router.back方法返回到指定页面时,原栈顶页面(包括)到指定页面(不包括)之间的所有页面栈都将从栈中弹出并销毁。
299>
300> 另外,如果使用router.back方法返回到原来的页面,原页面不会被重复创建,因此使用\@State声明的变量不会重复声明,也不会触发页面的aboutToAppear生命周期回调。如果需要在原页面中使用返回页面传递的自定义参数,可以在需要的位置进行参数解析。例如,在onPageShow生命周期回调中进行参数解析。
301
302
303## 页面返回前增加一个询问框
304
305在开发应用时,为了避免用户误操作或者丢失数据,有时候需要在用户从一个页面返回到另一个页面之前,弹出一个询问框,让用户确认是否要执行这个操作。
306
307本文将从[系统默认询问框](#系统默认询问框)和[自定义询问框](#自定义询问框)两个方面来介绍如何实现页面返回前增加一个询问框的功能。
308
309  **图3** 页面返回前增加一个询问框  
310
311![router-add-query-box-before-back](figures/router-add-query-box-before-back.gif)
312
313
314### 系统默认询问框
315
316为了实现这个功能,可以使用页面路由Router模块提供的两个方法:[router.showAlertBeforeBackPage](../reference/apis-arkui/js-apis-router.md#routershowalertbeforebackpage9)和[router.back](../reference/apis-arkui/js-apis-router.md#routerback)来实现这个功能。
317
318在使用页面路由Router相关功能之前,需要在代码中先导入Router模块。
319
320
321```ts
322import { router } from '@kit.ArkUI';
323```
324
325如果想要在目标界面开启页面返回询问框,需要在调用[router.back](../reference/apis-arkui/js-apis-router.md#routerback)方法之前,通过调用[router.showAlertBeforeBackPage](../reference/apis-arkui/js-apis-router.md#routershowalertbeforebackpage9)方法设置返回询问框的信息。例如,在支付页面中定义一个返回按钮的点击事件处理函数:
326
327```ts
328import { router } from '@kit.ArkUI';
329import { BusinessError } from '@kit.BasicServicesKit';
330
331// 定义一个返回按钮的点击事件处理函数
332function onBackClick(): void {
333  // 调用router.showAlertBeforeBackPage()方法,设置返回询问框的信息
334  try {
335    router.showAlertBeforeBackPage({
336      message: '您还没有完成支付,确定要返回吗?' // 设置询问框的内容
337    });
338  } catch (err) {
339    let message = (err as BusinessError).message
340    let code = (err as BusinessError).code
341    console.error(`Invoke showAlertBeforeBackPage failed, code is ${code}, message is ${message}`);
342  }
343
344  // 调用router.back()方法,返回上一个页面
345  router.back();
346}
347```
348
349其中,router.showAlertBeforeBackPage方法接收一个对象作为参数,该对象包含以下属性:
350
351message:string类型,表示询问框的内容。
352如果调用成功,则会在目标界面开启页面返回询问框;如果调用失败,则会抛出异常,并通过err.codeerr.message获取错误码和错误信息。
353
354当用户点击“返回”按钮时,会弹出确认对话框,询问用户是否确认返回。选择“取消”将停留在当前页目标页面;选择“确认”将触发router.back方法,并根据参数决定如何执行跳转。
355
356### 自定义询问框
357
358自定义询问框的方式,可以使用弹窗[promptAction.showDialog](../reference/apis-arkui/js-apis-promptAction.md#promptactionshowdialog)或者自定义弹窗实现。这样可以让应用界面与系统默认询问框有所区别,提高应用的用户体验度。本文以弹窗为例,介绍如何实现自定义询问框。
359
360在使用页面路由Router相关功能之前,需要在代码中先导入Router模块。
361
362
363```ts
364import { router } from '@kit.ArkUI';
365```
366
367在事件回调中,调用弹窗的[promptAction.showDialog](../reference/apis-arkui/js-apis-promptAction.md#promptactionshowdialog)方法:
368
369```ts
370import { promptAction, router } from '@kit.ArkUI';
371import { BusinessError } from '@kit.BasicServicesKit';
372
373function onBackClick() {
374  // 弹出自定义的询问框
375  promptAction.showDialog({
376    message: '您还没有完成支付,确定要返回吗?',
377    buttons: [
378      {
379        text: '取消',
380        color: '#FF0000'
381      },
382      {
383        text: '确认',
384        color: '#0099FF'
385      }
386    ]
387  }).then((result: promptAction.ShowDialogSuccessResponse) => {
388    if (result.index === 0) {
389      // 用户点击了“取消”按钮
390      console.info('User canceled the operation.');
391    } else if (result.index === 1) {
392      // 用户点击了“确认”按钮
393      console.info('User confirmed the operation.');
394      // 调用router.back()方法,返回上一个页面
395      router.back();
396    }
397  }).catch((err: Error) => {
398    let message = (err as BusinessError).message
399    let code = (err as BusinessError).code
400    console.error(`Invoke showDialog failed, code is ${code}, message is ${message}`);
401  })
402}
403```
404
405当用户点击“返回”按钮时,会弹出自定义的询问框,询问用户是否确认返回。选择“取消”将停留在当前页目标页面;选择“确认”将触发router.back方法,并根据参数决定如何执行跳转。
406
407## 命名路由
408
409在开发中为了跳转到共享包[HAR](../quick-start/har-package.md)或者[HSP](../quick-start/in-app-hsp.md)中的页面(即共享包中路由跳转),可以使用[router.pushNamedRoute](../reference/apis-arkui/js-apis-router.md#routerpushnamedroute10)来实现。
410
411  **图4** 命名路由跳转  
412
413![(figures/router-add-query-box-before-back.gif)](figures/namedroute-jump-to-mypage.gif)
414
415在使用页面路由Router相关功能之前,需要在代码中先导入Router模块。
416
417
418```ts
419import { router } from '@kit.ArkUI';
420```
421
422在想要跳转到的共享包[HAR](../quick-start/har-package.md)或者[HSP](../quick-start/in-app-hsp.md)页面里,给@Entry修饰的自定义组件[EntryOptions](../quick-start/arkts-create-custom-components.md#entryoptions10)命名:
423
424```ts
425// library/src/main/ets/pages/Index.ets
426// library为新建共享包自定义的名字
427@Entry({ routeName: 'myPage' })
428@Component
429export struct MyComponent {
430  build() {
431    Row() {
432      Column() {
433        Text('Library Page')
434          .fontSize(50)
435          .fontWeight(FontWeight.Bold)
436      }
437      .width('100%')
438    }
439    .height('100%')
440  }
441}
442```
443
444配置成功后需要在跳转的页面中引入命名路由的页面:
445
446```ts
447import { BusinessError } from '@kit.BasicServicesKit';
448import '@ohos/library/src/main/ets/pages/Index'; // 引入共享包中的命名路由页面
449
450@Entry
451@Component
452struct Index {
453  build() {
454    Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
455      Text('Hello World')
456        .fontSize(50)
457        .fontWeight(FontWeight.Bold)
458        .margin({ top: 20 })
459        .backgroundColor('#ccc')
460        .onClick(() => { // 点击跳转到其他共享包中的页面
461          try {
462            this.getUIContext().getRouter().pushNamedRoute({
463              name: 'myPage',
464              params: {
465                data1: 'message',
466                data2: {
467                  data3: [123, 456, 789]
468                }
469              }
470            })
471          } catch (err) {
472            let message = (err as BusinessError).message
473            let code = (err as BusinessError).code
474            console.error(`pushNamedRoute failed, code is ${code}, message is ${message}`);
475          }
476        })
477    }
478    .width('100%')
479    .height('100%')
480  }
481}
482```
483
484>**说明:**
485>
486>使用命名路由方式跳转时,需要在当前应用包的oh-package.json5文件中配置依赖。例如:
487>
488>```ts
489>"dependencies": {
490>    "@ohos/library": "file:../library",
491>    ...
492> }
493>```
494
495## 相关实例
496
497针对页面路由开发,有以下相关实例可供参考:
498
499- [页面布局和连接(ArkTS)(API9)](https://gitee.com/openharmony/applications_app_samples/tree/master/code/UI/ArkTsComponentCollection/DefiningPageLayoutAndConnection)