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