页面和路由 - 小程序是怎么跳转的?
🚀 页面跳转是小程序的基本功能,今天我们来学习如何在页面之间自由穿梭
什么是路由?
简单来说,路由就是页面之间的跳转规则。
想象一下:
- 你在首页点击"商品详情"按钮 → 跳转到商品详情页
- 在商品详情页点击"返回"按钮 → 回到首页
- 点击底部的"我的"标签 → 跳转到个人中心
这些跳转过程就是路由!
UniApp中的路由方式
UniApp提供了几种不同的跳转方式,每种都有不同的用途:
1. uni.navigateTo() - 普通跳转
用途:跳转到新页面,保留当前页面 特点:可以返回上一页
javascript
// 基本用法
uni.navigateTo({
url: '/pages/detail/detail'
})
// 带参数跳转
uni.navigateTo({
url: '/pages/detail/detail?id=123&name=商品名称'
})
// 带回调
uni.navigateTo({
url: '/pages/detail/detail',
success: function(res) {
console.log('跳转成功')
},
fail: function(err) {
console.log('跳转失败')
}
})
2. uni.redirectTo() - 替换跳转
用途:关闭当前页面,跳转到新页面 特点:不能返回上一页
javascript
uni.redirectTo({
url: '/pages/login/login'
})
使用场景:
- 登录成功后跳转到首页
- 支付完成后跳转到结果页
3. uni.reLaunch() - 重启应用
用途:关闭所有页面,打开指定页面 特点:清空页面栈
javascript
uni.reLaunch({
url: '/pages/index/index'
})
使用场景:
- 退出登录回到首页
- 切换用户身份
4. uni.switchTab() - 切换标签页
用途:跳转到tabBar页面 特点:只能跳转到配置了tabBar的页面
javascript
uni.switchTab({
url: '/pages/home/home'
})
5. uni.navigateBack() - 返回上一页
用途:返回上一页或指定页面
javascript
// 返回上一页
uni.navigateBack()
// 返回指定层数
uni.navigateBack({
delta: 2 // 返回2层
})
参数传递
1. URL参数传递
发送参数:
javascript
uni.navigateTo({
url: '/pages/detail/detail?id=123&name=iPhone&price=5999'
})
接收参数:
javascript
export default {
onLoad(options) {
console.log('商品ID:', options.id) // 123
console.log('商品名称:', options.name) // iPhone
console.log('商品价格:', options.price) // 5999
}
}
2. 复杂数据传递
对于复杂的数据(对象、数组),需要先转换:
发送复杂数据:
javascript
const product = {
id: 123,
name: 'iPhone',
specs: ['64GB', '128GB', '256GB']
}
uni.navigateTo({
url: '/pages/detail/detail?data=' + encodeURIComponent(JSON.stringify(product))
})
接收复杂数据:
javascript
export default {
onLoad(options) {
if (options.data) {
const product = JSON.parse(decodeURIComponent(options.data))
console.log(product)
}
}
}
实际案例:商品列表和详情
让我们做一个完整的例子:
1. 商品列表页面
vue
<template>
<view class="product-list">
<view
class="product-item"
v-for="product in products"
:key="product.id"
@click="goToDetail(product)"
>
<image :src="product.image" class="product-image"></image>
<view class="product-info">
<text class="product-name">\{\{ product.name \}\}</text>
<text class="product-price">¥\{\{ product.price \}\}</text>
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
products: [
{ id: 1, name: 'iPhone 15', price: 5999, image: '/static/iphone.jpg' },
{ id: 2, name: 'iPad Air', price: 4599, image: '/static/ipad.jpg' },
{ id: 3, name: 'MacBook Pro', price: 14999, image: '/static/macbook.jpg' }
]
}
},
methods: {
goToDetail(product) {
// 方式1:简单参数
uni.navigateTo({
url: `/pages/detail/detail?id=\${product.id}&name=\${product.name}&price=\${product.price}`
})
// 方式2:复杂数据
// uni.navigateTo({
// url: '/pages/detail/detail?data=' + encodeURIComponent(JSON.stringify(product))
// })
}
}
}
</script>
<style>
.product-list {
padding: 20px;
}
.product-item {
display: flex;
padding: 15px;
border-bottom: 1px solid #eee;
}
.product-image {
width: 80px;
height: 80px;
margin-right: 15px;
}
.product-info {
flex: 1;
}
.product-name {
font-size: 16px;
font-weight: bold;
display: block;
margin-bottom: 10px;
}
.product-price {
color: #ff6b6b;
font-size: 18px;
font-weight: bold;
}
</style>
2. 商品详情页面
vue
<template>
<view class="product-detail">
<image :src="productImage" class="detail-image"></image>
<view class="detail-info">
<text class="detail-name">\{\{ productName \}\}</text>
<text class="detail-price">¥\{\{ productPrice \}\}</text>
<view class="detail-actions">
<button @click="addToCart">加入购物车</button>
<button @click="buyNow" type="primary">立即购买</button>
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
productId: '',
productName: '',
productPrice: '',
productImage: '/static/default.jpg'
}
},
onLoad(options) {
// 接收参数
this.productId = options.id
this.productName = options.name
this.productPrice = options.price
// 根据ID加载更多商品信息
this.loadProductDetail()
},
methods: {
loadProductDetail() {
// 模拟加载商品详情
console.log('加载商品详情,ID:', this.productId)
// 这里可以调用API获取完整的商品信息
},
addToCart() {
uni.showToast({
title: '已加入购物车',
icon: 'success'
})
},
buyNow() {
// 跳转到订单页面
uni.navigateTo({
url: `/pages/order/order?productId=\${this.productId}`
})
}
}
}
</script>
<style>
.product-detail {
padding: 20px;
}
.detail-image {
width: 100%;
height: 300px;
margin-bottom: 20px;
}
.detail-name {
font-size: 20px;
font-weight: bold;
display: block;
margin-bottom: 10px;
}
.detail-price {
color: #ff6b6b;
font-size: 24px;
font-weight: bold;
display: block;
margin-bottom: 30px;
}
.detail-actions {
display: flex;
gap: 15px;
}
.detail-actions button {
flex: 1;
}
</style>
配置TabBar
如果你的应用需要底部导航,可以在pages.json
中配置:
json
{
"pages": [
{
"path": "pages/home/home",
"style": { "navigationBarTitleText": "首页" }
},
{
"path": "pages/category/category",
"style": { "navigationBarTitleText": "分类" }
},
{
"path": "pages/cart/cart",
"style": { "navigationBarTitleText": "购物车" }
},
{
"path": "pages/user/user",
"style": { "navigationBarTitleText": "我的" }
}
],
"tabBar": {
"color": "#7A7E83",
"selectedColor": "#3cc51f",
"borderStyle": "black",
"backgroundColor": "#ffffff",
"list": [
{
"pagePath": "pages/home/home",
"iconPath": "static/tab-home.png",
"selectedIconPath": "static/tab-home-active.png",
"text": "首页"
},
{
"pagePath": "pages/category/category",
"iconPath": "static/tab-category.png",
"selectedIconPath": "static/tab-category-active.png",
"text": "分类"
},
{
"pagePath": "pages/cart/cart",
"iconPath": "static/tab-cart.png",
"selectedIconPath": "static/tab-cart-active.png",
"text": "购物车"
},
{
"pagePath": "pages/user/user",
"iconPath": "static/tab-user.png",
"selectedIconPath": "static/tab-user-active.png",
"text": "我的"
}
]
}
}
路由拦截
有时候我们需要在跳转前做一些检查,比如登录验证:
javascript
// 在App.vue中添加全局路由拦截
export default {
onLaunch() {
// 重写navigateTo方法
const originalNavigateTo = uni.navigateTo
uni.navigateTo = function(options) {
// 需要登录的页面列表
const needLoginPages = ['/pages/user/user', '/pages/order/order']
if (needLoginPages.includes(options.url.split('?')[0])) {
// 检查是否已登录
const token = uni.getStorageSync('token')
if (!token) {
// 未登录,跳转到登录页
uni.navigateTo({
url: '/pages/login/login'
})
return
}
}
// 执行原始跳转
originalNavigateTo(options)
}
}
}
常见问题和解决方案
1. 页面栈溢出
问题:连续跳转太多页面导致栈溢出 解决:使用redirectTo
或reLaunch
替代navigateTo
2. 参数丢失
问题:页面跳转时参数丢失 解决:检查URL编码,使用encodeURIComponent
3. TabBar跳转失败
问题:使用navigateTo
跳转TabBar页面失败 解决:使用switchTab
跳转TabBar页面
小结
今天我们学习了:
- ✅ 5种不同的页面跳转方式
- ✅ 参数传递的方法
- ✅ TabBar的配置
- ✅ 路由拦截的实现
- ✅ 完整的商品列表和详情案例
记住这个口诀:
navigateTo
:普通跳转,可返回redirectTo
:替换跳转,不可返回reLaunch
:重启应用switchTab
:切换标签页navigateBack
:返回上一页
下一篇预告
下一篇我们将学习《生命周期详解 - 页面什么时候加载,什么时候销毁?》,深入了解页面和应用的生命周期。
练习作业
- 创建一个新闻列表页面,点击新闻跳转到详情页
- 在详情页显示新闻标题和内容
- 添加返回按钮
- 尝试配置一个简单的TabBar
路由是应用的骨架,掌握了路由,你就能构建复杂的多页面应用了!