HarmonyOS 5开发从入门到精通(五):页面路由与导航
HarmonyOS 5开发从入门到精通(五):页面路由与导航
一、路由系统基础概念
在HarmonyOS应用开发中,页面路由(Router)是实现多页面跳转和导航的核心机制。路由系统负责管理页面的堆栈、跳转动画、参数传递等关键功能。
1.1 页面路由的基本原理
HarmonyOS的路由系统基于页面堆栈(Page Stack)管理,支持前进、后退、替换、清空等多种操作模式。每个页面都是一个独立的组件,通过路由API进行跳转和通信。
核心特性:
- 支持页面间参数传递
- 提供多种跳转动画效果
- 支持页面返回结果回调
- 内置页面生命周期管理
二、基础路由操作
2.1 页面跳转与返回
import router from '@ohos.router';
// 首页组件
@Entry
@Component
struct HomePage {
build() {
Column({ space: 20 }) {
Text('首页')
.fontSize(24)
.fontWeight(FontWeight.Bold)
Button('跳转到详情页')
.onClick(() => {
// 跳转到详情页
router.pushUrl({
url: 'pages/DetailPage',
params: { id: 123, name: '测试商品' }
});
})
.width(200)
Button('跳转到设置页')
.onClick(() => {
router.pushUrl({
url: 'pages/SettingsPage'
});
})
.width(200)
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
.padding(20)
}
}
// 详情页组件
@Component
struct DetailPage {
@State id: number = 0
@State name: string = ''
aboutToAppear() {
// 接收路由参数
const params = router.getParams();
if (params) {
this.id = params['id'] as number;
this.name = params['name'] as string;
}
}
build() {
Column({ space: 20 }) {
Text(`商品ID: ${this.id}`)
.fontSize(18)
Text(`商品名称: ${this.name}`)
.fontSize(18)
Button('返回首页')
.onClick(() => {
router.back();
})
.width(200)
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
.padding(20)
}
}
2.2 路由跳转模式
HarmonyOS支持多种路由跳转模式,满足不同场景需求:
@Component
struct NavigationDemo {
build() {
Column({ space: 15 }) {
// 1. 标准跳转(压入堆栈)
Button('pushUrl - 标准跳转')
.onClick(() => {
router.pushUrl({
url: 'pages/TargetPage',
params: { type: 'push' }
});
})
.width(200)
// 2. 替换当前页(不保留历史)
Button('replaceUrl - 替换当前页')
.onClick(() => {
router.replaceUrl({
url: 'pages/TargetPage',
params: { type: 'replace' }
});
})
.width(200)
// 3. 清空堆栈并跳转
Button('clear - 清空堆栈并跳转')
.onClick(() => {
router.clear();
router.pushUrl({
url: 'pages/TargetPage',
params: { type: 'clear' }
});
})
.width(200)
// 4. 返回上一页
Button('back - 返回')
.onClick(() => {
router.back();
})
.width(200)
// 5. 返回指定页面
Button('backToPage - 返回指定页')
.onClick(() => {
router.backToPage({
url: 'pages/HomePage'
});
})
.width(200)
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
.padding(20)
}
}
三、路由参数传递
3.1 基本参数传递
// 发送参数
router.pushUrl({
url: 'pages/UserProfile',
params: {
userId: 1001,
userName: '张三',
userType: 'vip',
extraData: {
age: 25,
city: '深圳'
}
}
});
// 接收参数
@Component
struct UserProfile {
@State userId: number = 0
@State userName: string = ''
@State userType: string = ''
@State extraData: any = {}
aboutToAppear() {
const params = router.getParams();
if (params) {
this.userId = params['userId'] as number;
this.userName = params['userName'] as string;
this.userType = params['userType'] as string;
this.extraData = params['extraData'] || {};
}
}
build() {
Column({ space: 15 }) {
Text(`用户ID: ${this.userId}`)
.fontSize(18)
Text(`用户名: ${this.userName}`)
.fontSize(18)
Text(`用户类型: ${this.userType}`)
.fontSize(18)
Text(`年龄: ${this.extraData.age || '未知'}`)
.fontSize(16)
Text(`城市: ${this.extraData.city || '未知'}`)
.fontSize(16)
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
.padding(20)
}
}
3.2 返回结果回调
// 跳转并等待返回结果
router.pushUrl({
url: 'pages/SelectionPage',
params: { title: '请选择选项' }
}).then(() => {
// 页面返回时触发
const result = router.getParams();
if (result) {
console.log('返回结果:', result);
}
});
// 目标页面设置返回结果
@Component
struct SelectionPage {
@State options: string[] = ['选项A', '选项B', '选项C']
@State selectedOption: string = ''
build() {
Column({ space: 15 }) {
Text('请选择一个选项')
.fontSize(20)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 20 })
ForEach(this.options, (option) => {
Button(option)
.onClick(() => {
this.selectedOption = option;
// 返回并传递结果
router.back({
url: 'pages/SelectionPage',
params: { selected: option }
});
})
.width(200)
.margin({ bottom: 10 })
})
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
.padding(20)
}
}
四、路由动画效果
4.1 内置动画效果
HarmonyOS提供了丰富的页面跳转动画效果:
@Component
struct AnimationDemo {
build() {
Column({ space: 15 }) {
// 1. 无动画
Button('无动画')
.onClick(() => {
router.pushUrl({
url: 'pages/TargetPage',
animation: {
type: router.AnimationType.None
}
});
})
.width(200)
// 2. 默认动画(推入)
Button('默认推入动画')
.onClick(() => {
router.pushUrl({
url: 'pages/TargetPage',
animation: {
type: router.AnimationType.Push,
duration: 300
}
});
})
.width(200)
// 3. 淡入淡出
Button('淡入淡出')
.onClick(() => {
router.pushUrl({
url: 'pages/TargetPage',
animation: {
type: router.AnimationType.Fade,
duration: 400
}
});
})
.width(200)
// 4. 从底部滑入
Button('底部滑入')
.onClick(() => {
router.pushUrl({
url: 'pages/TargetPage',
animation: {
type: router.AnimationType.Slide,
curve: router.AnimationCurve.EaseOut
}
});
})
.width(200)
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
.padding(20)
}
}
4.2 自定义动画曲线
router.pushUrl({
url: 'pages/TargetPage',
animation: {
type: router.AnimationType.Push,
duration: 500,
curve: router.AnimationCurve.Spring // 弹簧效果
}
});
// 或者使用自定义贝塞尔曲线
router.pushUrl({
url: 'pages/TargetPage',
animation: {
type: router.AnimationType.Slide,
curve: {
type: router.AnimationCurve.Custom,
params: [0.42, 0, 0.58, 1] // 贝塞尔曲线参数
}
}
});
五、路由守卫与拦截
5.1 页面生命周期钩子
@Component
struct ProtectedPage {
@State isAuthenticated: boolean = false
// 页面即将显示
aboutToAppear() {
console.log('页面即将显示');
// 检查登录状态
this.checkAuth();
}
// 页面显示完成
onPageShow() {
console.log('页面显示完成');
}
// 页面即将隐藏
aboutToDisappear() {
console.log('页面即将隐藏');
}
// 页面隐藏完成
onPageHide() {
console.log('页面隐藏完成');
}
// 检查登录状态
private checkAuth() {
const isLoggedIn = AppStorage.Get<boolean>('isLoggedIn') || false;
if (!isLoggedIn) {
// 未登录,跳转到登录页
router.replaceUrl({
url: 'pages/LoginPage'
});
return;
}
this.isAuthenticated = true;
}
build() {
Column() {
if (this.isAuthenticated) {
Text('欢迎访问受保护页面')
.fontSize(24)
.fontWeight(FontWeight.Bold)
} else {
Text('检查登录状态...')
.fontSize(16)
.fontColor('#666')
}
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
}
}
5.2 路由拦截器
import router from '@ohos.router';
// 全局路由拦截器
router.addInterceptor({
// 路由跳转前拦截
beforePush: (to: router.RouterOptions, from: router.RouterOptions, next: router.NextCallback) => {
console.log('路由跳转前:', from?.url, '->', to.url);
// 检查是否需要登录
if (to.url === 'pages/ProfilePage' && !AppStorage.Get<boolean>('isLoggedIn')) {
// 跳转到登录页
router.pushUrl({
url: 'pages/LoginPage',
params: { redirect: to.url }
});
return;
}
// 允许跳转
next();
},
// 路由返回前拦截
beforeBack: (to: router.RouterOptions, from: router.RouterOptions, next: router.NextCallback) => {
console.log('路由返回前:', from?.url, '->', to?.url);
// 确认返回操作
if (from?.url === 'pages/EditPage') {
prompt.showDialog({
title: '确认返回',
message: '确定要放弃编辑内容吗?',
buttons: [
{
text: '取消',
color: '#666'
},
{
text: '确定',
color: '#007DFF',
action: () => {
next();
}
}
]
});
return;
}
next();
}
});
六、多页面应用实战
6.1 底部导航栏实现
// 底部导航栏组件
@Component
struct BottomTabBar {
@State currentTab: string = 'home'
private tabs = [
{ key: 'home', name: '首页', icon: $r('app.media.home') },
{ key: 'discover', name: '发现', icon: $r('app.media.discover') },
{ key: 'message', name: '消息', icon: $r('app.media.message') },
{ key: 'profile', name: '我的', icon: $r('app.media.profile') }
]
build() {
Row() {
ForEach(this.tabs, (tab) => {
Column() {
Image(tab.icon)
.width(24)
.height(24)
.objectFit(ImageFit.Contain)
.opacity(this.currentTab === tab.key ? 1 : 0.5)
Text(tab.name)
.fontSize(12)
.fontColor(this.currentTab === tab.key ? '#007DFF' : '#666')
}
.width('25%')
.height(56)
.justifyContent(FlexAlign.Center)
.onClick(() => {
this.currentTab = tab.key;
this.navigateToTab(tab.key);
})
})
}
.width('100%')
.height(56)
.backgroundColor(Color.White)
.border({ width: { top: 1 }, color: '#F0F0F0' })
}
private navigateToTab(tabKey: string) {
switch (tabKey) {
case 'home':
router.replaceUrl({ url: 'pages/HomePage' });
break;
case 'discover':
router.replaceUrl({ url: 'pages/DiscoverPage' });
break;
case 'message':
router.replaceUrl({ url: 'pages/MessagePage' });
break;
case 'profile':
router.replaceUrl({ url: 'pages/ProfilePage' });
break;
}
}
}
// 主页面框架
@Entry
@Component
struct MainApp {
@State currentPage: string = 'home'
build() {
Stack() {
// 页面内容区域
Column() {
if (this.currentPage === 'home') {
HomePage()
} else if (this.currentPage === 'discover') {
DiscoverPage()
} else if (this.currentPage === 'message') {
MessagePage()
} else if (this.currentPage === 'profile') {
ProfilePage()
}
}
.layoutWeight(1)
// 底部导航栏
Column() {
Blank()
BottomTabBar()
}
.alignItems(HorizontalAlign.Center)
}
.width('100%')
.height('100%')
}
}
6.2 路由堆栈管理
// 获取当前路由信息
const currentRoute = router.getState();
console.log('当前路由:', currentRoute);
// 获取路由堆栈
const stack = router.getLength();
console.log('路由堆栈长度:', stack);
// 清空路由历史
router.clear();
// 监听路由变化
router.on('routeChange', (to: router.RouterState, from: router.RouterState) => {
console.log('路由变化:', from?.name, '->', to.name);
});
// 移除路由监听
router.off('routeChange');
七、路由最佳实践
7.1 路由配置管理
// routes.ts - 路由配置文件
export const ROUTES = {
HOME: 'pages/HomePage',
LOGIN: 'pages/LoginPage',
PROFILE: 'pages/ProfilePage',
SETTINGS: 'pages/SettingsPage',
DETAIL: 'pages/DetailPage',
EDIT: 'pages/EditPage'
} as const;
// 路由跳转工具函数
export class RouterUtils {
// 跳转到首页
static toHome() {
router.replaceUrl({ url: ROUTES.HOME });
}
// 跳转到登录页
static toLogin(redirect?: string) {
router.pushUrl({
url: ROUTES.LOGIN,
params: { redirect }
});
}
// 跳转到详情页
static toDetail(id: number, name: string) {
router.pushUrl({
url: ROUTES.DETAIL,
params: { id, name }
});
}
// 返回上一页
static back() {
router.back();
}
// 返回首页
static backToHome() {
router.backToPage({ url: ROUTES.HOME });
}
}
7.2 性能优化建议
- 避免频繁的路由跳转
// 不推荐:频繁跳转
@State count: number = 0
onClick() {
this.count++;
if (this.count > 5) {
router.pushUrl({ url: 'pages/TargetPage' });
}
}
// 推荐:防抖处理
private debounceTimer: number = 0
onClick() {
this.count++;
clearTimeout(this.debounceTimer);
this.debounceTimer = setTimeout(() => {
if (this.count > 5) {
router.pushUrl({ url: 'pages/TargetPage' });
}
}, 300);
}
- 合理使用路由缓存
// 在页面组件中缓存数据
@Component
struct CachedPage {
@State cachedData: any = null
aboutToAppear() {
if (!this.cachedData) {
// 首次加载数据
this.loadData();
}
}
private loadData() {
// 模拟网络请求
setTimeout(() => {
this.cachedData = { id: 1, name: '缓存数据' };
}, 1000);
}
}
- 页面懒加载
// 使用动态导入实现懒加载
const LazyComponent = lazy(() => import('./LazyPage'));
@Entry
@Component
struct MainPage {
build() {
Column() {
Button('加载懒加载页面')
.onClick(() => {
router.pushUrl({ url: 'pages/LazyPage' });
})
}
}
}
八、总结
通过本篇教程,您已经掌握了:
✅ 路由系统的基本原理和核心API
✅ 页面跳转与参数传递的各种方式
✅ 路由动画效果的配置和使用
✅ 路由守卫与拦截的实现方法
✅ 多页面应用的架构设计
✅ 性能优化和最佳实践
关键知识点回顾:
- 使用
router.pushUrl进行页面跳转,router.back返回 - 通过
router.getParams获取路由参数 - 支持多种跳转动画效果和自定义曲线
- 页面生命周期钩子用于权限控制和数据加载
- 路由拦截器实现全局路由守卫
下一篇我们将学习网络请求与数据管理,掌握如何从服务器获取数据并在应用中展示。建议您动手实践本文中的路由案例,特别是多页面应用和路由守卫部分,这将帮助您深入理解HarmonyOS路由系统的各种特性和使用场景。
浙公网安备 33010602011771号