畅行石化app连云港石化基地
116.69MB · 2025-11-10
本篇学习路径:
flowchart TD
A[页面路由与导航] --> B[页面栈管理原理]
A --> C[路由跳转方式对比]
A --> D[传参与接收参数]
A --> E[导航栏自定义配置]
B --> B1[栈数据结构<br>LIFO后进先出]
B --> B2[getCurrentPages<br>获取页面栈实例]
C --> C1[navigateTo<br>保留当前页面跳转]
C --> C2[redirectTo<br>关闭当前页面跳转]
C --> C3[switchTab<br>跳转tabBar页面]
C --> C4[reLaunch<br>关闭所有页面跳转]
C --> C5[navigateBack<br>返回上一页面]
D --> D1[URL拼接传参<br>?key=value&key2=value2]
D --> D2[onLoad生命周期接收<br>options参数对象]
E --> E1[pages.json全局配置<br>navigationBarTitleText等]
E --> E2[页面级配置<br>覆盖全局配置]
E --> E3[动态修改<br>setNavigationBarTitle]
%% 连接关系
B2 -.-> C1
B2 -.-> C5
D1 -.-> C1
D1 -.-> C2
D1 -.-> C3
D1 -.-> C4
E3 -.-> D2
%% 样式定义
classDef root fill:#1a2a6c,color:#fff,stroke:#1a2a6c,stroke-width:2px
classDef level1 fill:#b21f1f,color:#fff,stroke:#b21f1f
classDef level2 fill:#fdbb2d,color:#333,stroke:#fdbb2d
classDef level3 fill:#4CAF50,color:#fff,stroke:#4CAF50
class A root
class B,C,D,E level1
class B1,B2,C1,C2,C3,C4,C5,D1,D2,E1,E2,E3 level2
在深入各种跳转API之前,我们必须先理解一个核心概念——页面栈(Page Stack)。这是后面所讲内容的基础。
之前讲解Flutter时已经提到了,页面栈就好比一摞书。
在uni-app中,这个“放书”和“拿书”的过程,就是由页面栈来管理的。它是一个遵循 “后进先出(LIFO - Last In, First Out)” 原则的数据结构。
uni.navigateTo 的方法打开一个新页面时,这个新页面就会被压入栈顶,成为活动页面(Active Page),展示给用户。uni.navigateBack 方法时,栈顶的页面就会被弹出,此时,下面的页面就会成为新的活动页面,呈现给用户。flowchart TD
subgraph A [初始状态]
direction TB
subgraph StackA [页面栈]
A1[页面A]:::top
end
StateA[用户正 viewing 页面A]
end
A -->|用户执行 uni.navigateTo<br>跳转到 页面B| B
subgraph B [navigateTo 后]
direction TB
subgraph StackB [页面栈]
B1[页面B]:::top
B2[页面A]
end
StateB[用户正 viewing 页面B]
end
B -->|用户执行 uni.navigateTo<br>跳转到 页面C| C
subgraph C [再次 navigateTo 后]
direction TB
subgraph StackC [页面栈]
C1[页面C]:::top
C2[页面B]
C3[页面A]
end
StateC[用户正 viewing 页面C]
end
C -->|用户点击返回按钮<br>或执行 uni.navigateBack| D
subgraph D [navigateBack 后]
direction TB
subgraph StackD [页面栈]
D1[页面B]:::top
D2[页面A]
end
StateD[用户正 viewing 页面B<br>页面C已被销毁]
end
classDef top fill:#e1f5fe;
一个简单的例子:
首页(Index) -> 商品列表(List) -> 商品详情(Detail)
这个过程的栈变化是:
[Index][Index, List] (List被Push入栈)[Index, List, Detail] (Detail被Push入栈)[Index, List] (Detail被Pop出栈)[Index] (List被Pop出栈)uni-app 提供了一个全局的 getCurrentPages() 方法来获取当前页面栈的实例数组。
这个数组:
在任何页面中,你都可以这样获取栈信息:
// 在商品详情页(Detail.vue)的 methods 中
checkPageStack() {
// 获取当前页面栈的实例数组
const pages = getCurrentPages();
console.log('当前页面栈:', pages);
// 获取栈顶页面实例
const currentPage = pages[pages.length - 1];
console.log('当前页面实例:', currentPage);
// 获取栈底页面实例(一般来说是首页)
const firstPage = pages[0];
console.log('首页实例:', firstPage);
// 甚至可以获取上一个页面的实例,并直接调用其方法或数据
const prevPage = pages[pages.length - 2]; // 上一个页面
if (prevPage) {
console.log('上一个页面:', prevPage);
// 注意:这是一种页面间通信的方式,但需谨慎使用,确保prevPage存在且结构稳定
// prevPage.someData = '来自详情页的数据'; // 直接修改上一个页面的数据
// prevPage.someMethod(); // 直接调用上一个页面的方法
}
}
getCurrentPages() 的应用场景:
首页->分类->商品列表->商品详情->订单确认->支付结果),想提供一个“一键回首页”的按钮。
goHome() {
// 获取页面栈
const pages = getCurrentPages();
// 如果栈长度大于1,说明不在首页,可以返回
if (pages.length > 1) {
// uni.reLaunch 会关闭所有页面,打开首页
uni.reLaunch({
url: '/pages/index/index'
});
} else {
uni.showToast({
title: '已经在首页了',
icon: 'none'
});
}
}
// 在详情页(Detail.vue)
saveAndBack() {
// 获取上一个页面(列表页)的实例
const pages = getCurrentPages();
const listPage = pages[pages.length - 2];
// 调用列表页定义的刷新数据方法
if (listPage && listPage.refreshList) {
listPage.refreshList();
}
// 返回
uni.navigateBack();
}
uni-app 提供了5种核心的路由跳转API,它们对应着不同的页面栈操作和业务场景。选对了API,用户体验才能流畅自然。
| 方法 | 作用 | 页面栈变化 | 适用场景 |
|---|---|---|---|
uni.navigateTo | 保留当前页面,跳转到新页面 | Push新页面 | 最常用的跳转,如列表->详情 |
uni.redirectTo | 关闭当前页面,跳转到新页面 | Replace当前页面 | 中断当前流程,如登录页跳首页 |
uni.reLaunch | 关闭所有页面,打开新页面 | 关闭所有旧页面,Open新页面 | 应用重启,如切换用户身份 |
uni.switchTab | 跳转到 tabBar 页面 | 关闭所有非tabBar页面 | 切换底部导航 |
uni.navigateBack | 返回上一页面或多步 | Pop出一个或多个页面 | 返回上一级或首页 |
下面我们通过代码来详细讲述。
uni.navigateTo这是你最常用的跳转方式。它会在页面栈中新增一个记录,原页面会被保留。
特点:
具体代码示例:
// 在列表页(List.vue)中跳转到详情页(Detail.vue)
goToDetail(productId) {
// 使用 uni.navigateTo 进行跳转
uni.navigateTo({
// 目标页面的路径,支持传递参数
url: `/pages/detail/detail?id=${productId}&name=test`,
// 跳转成功回调
success: (res) => {
console.log('跳转成功!', res);
},
// 跳转失败回调
fail: (err) => {
console.error('跳转失败:', err);
uni.showToast({
title: '跳转失败,请重试',
icon: 'none'
});
},
// 跳转结束的回调
complete: () => {
console.log('跳转动作完成,无论成功还是失败都会调用');
}
});
}
对应的页面栈变化过程图解:
flowchart TD
Start[列表页 List] -->|执行<br>uni.navigateTo| Action[将详情页 Detail Push 入栈]
Action --> Result[栈变为: Index, List, Detail]
Result --> View[用户看到 Detail 页<br>List 被保留在栈中]
uni.redirectTo有时,你希望用户跳转到新页面后,不能再返回原页面。这时就需要 redirectTo。它用新页面替换当前页面,当前页面会被销毁。
特点:
具体代码示例:
// 在 splash 页或登录页,应用启动后判断用户状态
onLoad() {
// 检查用户是否已登录
const isLoggedIn = checkLoginStatus(); // 假设的检查方法
if (isLoggedIn) {
// 如果已登录,直接重定向到首页,并且不能返回到这个splash页
uni.redirectTo({
url: '/pages/index/index'
});
} else {
// 如果未登录,跳转到登录页
uni.navigateTo({
url: '/pages/login/login'
});
}
}
// 在登录页(Login.vue),登录成功后
handleLoginSuccess() {
// 登录成功后,用首页替换掉登录页,用户无法再返回到登录页
uni.redirectTo({
url: '/pages/index/index'
});
}
对应的页面栈变化:
[Splash]redirectTo(Home) 后,新栈:[Home] (Splash被替换掉了)uni.reLaunch这个API会关闭所有已打开的页面,然后打开一个新的页面。相当于重启了一次应用。
特点:
具体代码示例:
// 场景1:在深页面路径中,提供“返回首页”功能,并重置应用状态
goHomeHard() {
uni.reLaunch({
url: '/pages/index/index'
});
}
// 场景2:用户切换账号后,需要清空所有之前的页面状态,进入首页
afterSwitchAccount() {
uni.reLaunch({
url: '/pages/index/index'
});
}
对应的页面栈变化:
[Index, List, Detail, Cart, Order, Payment] (一个非常深的链条)reLaunch(Home) 后,新栈:[Home] (所有旧页面全部被销毁)uni.switchTab用于跳转到 pages.json 中配置的 tabBar 页面,并关闭其他所有非 tabBar 页面。
重要特性:
tabBar 中定义。switchTab,原 tabBar 页面的 onShow 会触发,但 onLoad 不会再次触发。具体代码示例:
比方说我们的 pages.json 配置了首页(index)和个人中心(profile)两个tab。
{
"tabBar": {
"list": [
{
"pagePath": "pages/index/index",
"text": "首页"
},
{
"pagePath": "pages/profile/profile",
"text": "我的"
}
]
}
}
// 在任何一个非tabBar页面(如商品详情页 Detail.vue)想切回首页
goToIndexTab() {
uni.switchTab({
url: '/pages/index/index' // 这个页面必须在 tabBar 的 list 中
});
}
对应的页面栈变化:
[Index (tab), List, Detail] (Index是tab页,但当前显示的是Detail)switchTab(Index) 后,新栈:[Index (tab)] (List和Detail被全部关闭)uni.navigateBack用于返回上一页面或多级页面。它是唯一一个不指定目标url的导航API,因为它默认就是往回走。
核心参数:delta
delta: 表示返回的页面层数,默认为 1(返回上一页)。具体代码示例:
// 1. 最简单的返回上一页
goBack() {
uni.navigateBack();
// 相当于 uni.navigateBack({ delta: 1 });
}
// 2. 返回两级页面
// 假设页面栈是 [A, B, C],在C页面调用此方法,将直接返回到A页面
goBackTwoLevels() {
uni.navigateBack({
delta: 2
});
}
// 3. 结合 getCurrentPages(),实现返回
// 比方说在分享进来的页面,如果只有一层,则返回时直接回到首页
smartBack() {
const pages = getCurrentPages();
if (pages.length === 1) {
// 如果只有一页,说明是分享直链进来的,返回首页
uni.switchTab({
url: '/pages/index/index'
});
} else {
// 否则正常返回
uni.navigateBack();
}
}
五种路由方式综合对比流程图:
flowchart TD
Start[起始页面栈: A, B, C<br>当前页为 C]
Start --> NavigateTo[uni.navigateTo<br>跳转到 D]
NavigateTo --> ResultNT[新栈: A, B, C, D]
Start --> RedirectTo[uni.redirectTo<br>用 D 替换 C]
RedirectTo --> ResultRT[新栈: A, B, D]
Start --> SwitchTab[uni.switchTab<br>跳转到 Tab页 T]
SwitchTab --> ResultST[新栈: T<br>清空所有非Tab页]
Start --> ReLaunch[uni.reLaunch<br>重启到新页 R]
ReLaunch --> ResultRL[新栈: R<br>清空所有旧页]
Start --> NavigateBack[uni.navigateBack<br>delta=1]
NavigateBack --> ResultNB[新栈: A, B<br>C 被弹出]
页面跳转往往不是孤立的,需要携带信息。比如从列表页跳转到详情页,必须告诉详情页:“你要展示哪个商品的ID”。
在 navigateTo, redirectTo 等方法的 url 中,可以像普通URL一样拼接参数。
具体示例如下:
// 在列表页传递商品ID和名称
goToDetail(product) {
uni.navigateTo({
// ? 开始代表增加了参数,多个参数用 & 连接
// 注意:参数值最好使用 encodeURIComponent 进行编码,防止特殊字符(如&)导致问题
url: `/pages/detail/detail?id=${product.id}&name=${encodeURIComponent(product.name)}&type=physical`
});
}
在目标页面的 onLoad 生命周期函数中,可以接收到一个 options 对象,里面包含了所有传递过来的参数。
为什么是 onLoad?
因为 onLoad 是页面首次加载时才会触发的生命周期。通过路由跳转并传递参数,对于目标页面来说就是一次“首次加载”。
具体代码如下:
// 在详情页(Detail.vue)中
export default {
data() {
return {
productId: '',
productName: '',
productType: ''
};
},
onLoad(options) {
// options 就是一个对象,包含了 url 中传递的所有参数
console.log('接收到的参数:', options);
// 将参数赋值给页面的 data,供模板或方法使用
this.productId = options.id;
this.productName = options.name; // 如果传递时编码了,这里可能需要 decodeURIComponent
this.productType = options.type || 'digital'; // 设置默认值,防止未传参
// 根据获取到的 id,去请求详细的商品数据
this.fetchProductDetail(this.productId);
},
methods: {
fetchProductDetail(id) {
//根据 id 获取商品详情
console.log(`开始请求商品ID为 ${id} 的详情数据...`);
}
}
};
一个完整的传参/接参流程:
sequenceDiagram
participant L as 列表页(List.vue)
participant D as 详情页(Detail.vue)
L->>L: 用户点击商品,<br>触发 goToDetail 方法
L->>D: uni.navigateTo({<br>url: '/pages/detail/detail?id=123&name=Phone' })
Note over D: 页面初始化<br>触发 onLoad 生命周期
D->>D: onLoad(options) 执行<br>options = { id: '123', name: 'Phone' }
D->>D: 将 options.id 赋值给<br>this.productId
D->>D: 调用 this.fetchProductDetail('123')
导航栏提供了强大的配置能力,可以在 pages.json 中对其进行全局和页面的个性化设置。
配置遵循“就近原则”:页面级配置的优先级高于全局配置。
pages.json 结构:
{
// 1. 全局配置 - 对所有页面生效
"globalStyle": {
"navigationBarTitleText": "我的默认App", // 导航标题
"navigationBarBackgroundColor": "#007AFF", // 导航栏背景色
"navigationBarTextStyle": "white", // 标题颜色
"backgroundColor": "#F8F8F8", // 背景色
"enablePullDownRefresh": false // 是否开启下拉刷新
},
// 2. 页面路由列表
"pages": [
{
"path": "pages/index/index",
"style": {
"navigationBarTitleText": "首页"
}
},
{
"path": "pages/profile/profile",
"style": {
"navigationBarTitleText": "个人中心",
"navigationBarBackgroundColor": "#4CD964",
"enablePullDownRefresh": true
}
},
{
"path": "pages/detail/detail",
"style": {
"navigationBarTitleText": "商品详情",
// 隐藏原生导航栏,实现全屏或自定义导航栏
"navigationStyle": "custom"
}
}
],
// 3. tabBar 配置
"tabBar": {
"color": "#7A7E83",
"selectedColor": "#007AFF",
"backgroundColor": "#FFFFFF",
"list": [
// tabBar列表 ...
]
}
}
很多时候,我们需要在页面加载后,根据数据动态设置标题。例如,在商品详情页,标题应该是商品的名字,这个名字来自后台接口。
核心API:uni.setNavigationBarTitle
代码示例:
// 在详情页(Detail.vue)中
export default {
onLoad(options) {
this.productId = options.id;
this.fetchProductDetail();
},
methods: {
async fetchProductDetail() {
// 模拟接口请求
const res = await this.$api.getProductDetail(this.productId);
if (res.success) {
const product = res.data;
// 动态设置导航栏标题为商品名称
uni.setNavigationBarTitle({
title: product.name // 从接口获取的商品名
});
}
}
}
};
"navigationStyle": "custom")当uni-app自带的导航栏样式无法满足你的设计需求时(比如需要加入搜索框、按钮、或者需要特殊的背景效果),你可以选择自定义导航栏。
配置步骤:
pages.json 中为指定页面启用自定义导航栏:
{
"path": "pages/my-custom-page/my-custom-page",
"style": {
"navigationStyle": "custom"
}
}
<template> 中,自己编写导航栏的UI结构:
<template>
<view>
<!-- 自定义导航栏 -->
<view class="custom-navbar">
<!-- 状态栏占位,防止内容冲到状态栏下面 -->
<view class="status-bar"></view>
<!-- 导航栏内容区 -->
<view class="navbar-content">
<text class="nav-title">我的自定义标题</text>
<button class="nav-btn">按钮</button>
</view>
</view>
<!-- 页面内容,需要给自定义导航栏留出高度 -->
<view class="page-content">
...
</view>
</view>
</template>
<style> 中编写样式,并处理好高度问题:
<style scoped>
/* 1. 状态栏高度占位 */
.status-bar {
height: var(--status-bar-height);
width: 100%;
}
/* 2. 自定义导航栏内容区 */
.navbar-content {
height: 44px; /* 导航栏标准高度 */
background-color: #007AFF;
display: flex;
align-items: center;
justify-content: center;
position: relative;
color: white;
}
/* 3. 页面内容需要有一个上边距 */
.page-content {
margin-top: calc(var(--status-bar-height) + 44px); /* 状态栏高度 + 导航栏高度 */
}
</style>
自定义导航栏的优缺点:
至此我们已经把uni-app路由导航的核心知识体系全部梳理完毕了。最后做一个整体总结,并提供一个综合小案例。
页面栈(Page Stack)
getCurrentPages(),用于获取当前栈实例,可进行跨页面通信和智能导航。五大路由跳转方法
uni.navigateTo:保留当前页,跳转新页。最常用。uni.redirectTo:关闭当前页,跳转新页。用于中断当前流程,不让返回。uni.reLaunch:关闭所有页,打开新页。用于重启应用状态。uni.switchTab:跳转Tab页,关闭所有非Tab页。专为底部导航设计。uni.navigateBack:返回上一页或多页。最简单的返回操作。参数传递与接收
url 后通过 ?key=value&key2=value2 的形式拼接。onLoad(options) 生命周期中,从 options 参数中获取。导航栏自定义
pages.json 的 globalStyle 和每个页面的 style 中配置标题、颜色等。uni.setNavigationBarTitle API。"navigationStyle": "custom",然后自己编写视图和样式,注意处理好状态栏高度。让我们用刚学的知识,模拟一个简单的电商页面跳转流程。以下只写核心思路代码,具体细节代码需要自行发挥!!!
// 1. 首页 (index.vue) - 点击商品分类
goToCategory(categoryId) {
uni.navigateTo({
url: `/pages/category/category?id=${categoryId}`
});
}
// 2. 分类页 (category.vue) - 接收分类ID,展示商品列表
onLoad(options) {
this.categoryId = options.id;
this.fetchGoodsList();
},
// 点击列表中的商品
onGoodsItemTap(goodsId) {
uni.navigateTo({
url: `/pages/goods-detail/goods-detail?goods_id=${goodsId}`
});
}
// 3. 商品详情页 (goods-detail.vue) - 接收商品ID,展示详情
onLoad(options) {
this.goodsId = options.goods_id;
this.fetchGoodsDetail().then(goods => {
// 动态设置导航栏标题为商品名
uni.setNavigationBarTitle({ title: goods.name });
});
},
// 点击加入购物车
addToCart() {
uni.showToast({
title: '添加成功'
});
},
// 点击立即购买
buyNow() {
// 跳转到订单确认页,并不允许返回到此详情页
uni.redirectTo({
url: `/pages/order-confirm/order-confirm?goods_id=${this.goodsId}`
});
}
// 4. 订单确认页 (order-confirm.vue)
onLoad(options) {
this.goodsId = options.goods_id;
},
// 提交订单成功
onOrderSubmitSuccess(orderId) {
// 关闭所有页面,跳转到订单结果页
uni.reLaunch({
url: `/pages/order-result/order-result?order_id=${orderId}&status=success`
});
}
// 5. 在订单结果页 (order-result.vue),提供“返回首页”的按钮
backToHome() {
// 因为是用 reLaunch 跳过来的,栈里只有这一页,所以用 switchTab 回首页
uni.switchTab({
url: '/pages/index/index'
});
}
路由与导航,作为连接整个应用的脉络,其重要性不言而喻。理解页面栈的原理,能让你在遇到复杂的导航问题时游刃有余;熟练掌握五种跳转方式,能让你为用户设计出最符合直觉的交互流程; 希望这篇文章能够帮到你你,最后如果觉得文章写得还可以别忘了‘一键三连’(点赞、关注、收藏),这是对我创作的最大鼓励!
有任何问题,欢迎在评论区留言,我们下篇见!