路由與頁面跳轉
概述
uni-app 提供了一套完整的路由系統,支援多種頁面跳轉方式。瞭解路由機制是開發 uni-app 應用的基礎。
路由配置
pages.json 配置
路由配置主要在 pages.json 檔案中進行:
json
{
"pages": [
{
"path": "pages/index/index",
"style": {
"navigationBarTitleText": "首頁"
}
},
{
"path": "pages/detail/detail",
"style": {
"navigationBarTitleText": "詳情頁"
}
},
{
"path": "pages/user/user",
"style": {
"navigationBarTitleText": "使用者中心"
}
}
],
"tabBar": {
"color": "#7A7E83",
"selectedColor": "#007AFF",
"backgroundColor": "#ffffff",
"list": [
{
"pagePath": "pages/index/index",
"iconPath": "static/tabbar/home.png",
"selectedIconPath": "static/tabbar/home-active.png",
"text": "首頁"
},
{
"pagePath": "pages/user/user",
"iconPath": "static/tabbar/user.png",
"selectedIconPath": "static/tabbar/user-active.png",
"text": "我的"
}
]
}
}路由規則
- 首頁規則:
pages陣列的第一個頁面為應用首頁 - 路徑規則:路徑不包含副檔名,使用相對路徑
- 樣式配置:每個頁面可以單獨配置導航欄樣式
頁面跳轉方式
1. 導航到新頁面
uni.navigateTo
保留當前頁面,跳轉到應用內的某個頁面。
javascript
// 基本用法
uni.navigateTo({
url: '/pages/detail/detail'
})
// 帶引數跳轉
uni.navigateTo({
url: '/pages/detail/detail?id=123&title=測試標題'
})
// 使用物件傳參
uni.navigateTo({
url: '/pages/detail/detail',
success: (res) => {
console.log('跳轉成功')
},
fail: (err) => {
console.error('跳轉失敗', err)
}
})引數接收
在目標頁面的 onLoad 生命週期中接收引數:
javascript
export default {
onLoad(options) {
console.log('頁面引數:', options)
this.id = options.id
this.title = options.title
}
}2. 重定向到新頁面
uni.redirectTo
關閉當前頁面,跳轉到應用內的某個頁面。
javascript
uni.redirectTo({
url: '/pages/login/login'
})適用場景:
- 登入頁面跳轉
- 許可權驗證失敗跳轉
- 表單提交後跳轉
3. 返回上一頁
uni.navigateBack
關閉當前頁面,返回上一頁面或多級頁面。
javascript
// 返回上一頁
uni.navigateBack()
// 返回指定層級
uni.navigateBack({
delta: 2 // 返回上兩頁
})
// 帶引數返回(需要特殊處理)
uni.$emit('pageBack', { data: '返回資料' })
uni.navigateBack()接收返回資料
在前一頁面監聽事件:
javascript
export default {
onShow() {
// 監聽返回事件
uni.$on('pageBack', (data) => {
console.log('收到返回資料:', data)
})
},
onHide() {
// 移除事件監聽
uni.$off('pageBack')
}
}4. 切換 Tab 頁面
uni.switchTab
跳轉到 tabBar 頁面,並關閉其他所有非 tabBar 頁面。
javascript
uni.switchTab({
url: '/pages/user/user'
})注意事項:
- 只能跳轉到 tabBar 配置的頁面
- 無法傳遞引數
- 會關閉所有非 tabBar 頁面
5. 重新載入當前頁面
uni.reLaunch
關閉所有頁面,開啟應用內的某個頁面。
javascript
uni.reLaunch({
url: '/pages/index/index'
})適用場景:
- 應用重新初始化
- 使用者登出後跳轉
- 重大錯誤恢復
路由傳參方式
1. URL 查詢字串
最常用的傳參方式:
javascript
// 傳遞引數
uni.navigateTo({
url: '/pages/detail/detail?id=123&name=張三&type=user'
})
// 接收引數
export default {
onLoad(options) {
this.id = options.id // "123"
this.name = options.name // "張三"
this.type = options.type // "user"
}
}優點:簡單易用,支援所有跳轉方式 缺點:只能傳遞字串型別,長度有限制
2. 全域事件匯流排
使用 uni.$emit 和 uni.$on 進行通訊:
javascript
// 頁面A:傳遞資料
uni.$emit('pageData', {
id: 123,
name: '張三',
data: { complex: 'object' }
})
uni.navigateTo({
url: '/pages/detail/detail'
})
// 頁面B:接收資料
export default {
onLoad() {
uni.$once('pageData', (data) => {
this.processData(data)
})
}
}優點:支援複雜資料型別,靈活性高 缺點:需要手動管理事件監聽
3. 全域狀態管理
使用 Vuex 進行狀態管理:
javascript
// store/modules/page.js
export default {
state: {
pageParams: {}
},
mutations: {
setPageParams(state, params) {
state.pageParams = params
},
clearPageParams(state) {
state.pageParams = {}
}
}
}
// 頁面A:設定引數
this.$store.commit('setPageParams', {
id: 123,
data: { complex: 'object' }
})
uni.navigateTo({
url: '/pages/detail/detail'
})
// 頁面B:獲取引數
export default {
onLoad() {
const params = this.$store.state.page.pageParams
this.processParams(params)
// 清理引數
this.$store.commit('clearPageParams')
}
}優點:資料持久化,支援複雜場景 缺點:需要配置 Vuex,稍顯複雜
4. 本地儲存
使用 uni.setStorage 進行資料傳遞:
javascript
// 頁面A:儲存資料
uni.setStorage({
key: 'pageParams',
data: {
id: 123,
name: '張三'
}
})
uni.navigateTo({
url: '/pages/detail/detail'
})
// 頁面B:讀取資料
export default {
onLoad() {
uni.getStorage({
key: 'pageParams',
success: (res) => {
this.processData(res.data)
// 清理資料
uni.removeStorage({
key: 'pageParams'
})
}
})
}
}優點:資料持久化,支援離線場景 缺點:非同步操作,需要處理錯誤
路由守衛
自定義路由守衛
雖然 uni-app 沒有內建的路由守衛,但可以透過封裝實現類似功能:
javascript
// utils/router.js
export const router = {
// 路由跳轉前的守衛
beforeEach(callback) {
this.beforeEachCallback = callback
},
// 封裝 navigateTo
navigateTo(options) {
if (this.beforeEachCallback) {
const result = this.beforeEachCallback(options)
if (result === false) {
return Promise.reject(new Error('路由跳轉被阻止'))
}
if (typeof result === 'object') {
options = { ...options, ...result }
}
}
return new Promise((resolve, reject) => {
uni.navigateTo({
...options,
success: (res) => {
resolve(res)
},
fail: (err) => {
reject(err)
}
})
})
}
}
// 使用路由守衛
router.beforeEach((to) => {
// 檢查登入狀態
if (to.url.includes('/user/') && !isLogin()) {
uni.showToast({
title: '請先登入',
icon: 'none'
})
// 跳轉到登入頁
uni.redirectTo({
url: '/pages/login/login'
})
return false
}
// 新增通用引數
return {
url: to.url + '?timestamp=' + Date.now()
}
})
// 使用封裝後的路由
router.navigateTo({
url: '/pages/user/profile'
})許可權控制
javascript
// 許可權檢查函數
function checkPermission(pageUrl) {
const publicPages = ['/pages/index/index', '/pages/login/login']
// 公開頁面直接放行
if (publicPages.includes(pageUrl)) {
return true
}
// 檢查使用者許可權
const userInfo = getStorage('userInfo')
if (!userInfo) {
return false
}
// 根據使用者角色檢查許可權
const userRole = userInfo.role
const pagePermissions = getPagePermissions(pageUrl)
return pagePermissions.includes(userRole)
}
// 路由守衛
router.beforeEach((to) => {
if (!checkPermission(to.url)) {
uni.showToast({
title: '無許可權訪問',
icon: 'none'
})
return false
}
})路由動畫
配置頁面動畫
在 pages.json 中配置頁面切換動畫:
json
{
"pages": [
{
"path": "pages/index/index",
"style": {
"navigationBarTitleText": "首頁",
"animationType": "slide-in-right",
"animationDuration": 300
}
}
],
"globalStyle": {
"animationType": "pop-in",
"animationDuration": 300
}
}動畫型別
- slide-in-right:從右側滑入
- slide-in-left:從左側滑入
- slide-in-top:從頂部滑入
- slide-in-bottom:從底部滑入
- fade-in:淡入
- zoom-out:縮放淡出
- zoom-fade-out:縮放淡出
- pop-in:彈出
自定義動畫
javascript
// 使用 CSS 動畫
.custom-animation {
animation: slideIn 0.3s ease-out;
}
@keyframes slideIn {
from {
transform: translateX(100%);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}路由最佳實踐
1. 路由管理
javascript
// utils/router.js
const routes = {
HOME: '/pages/index/index',
LOGIN: '/pages/login/login',
USER_PROFILE: '/pages/user/profile',
PRODUCT_DETAIL: '/pages/product/detail'
}
export const navigateTo = (route, params = {}) => {
let url = routes[route] || route
if (params && Object.keys(params).length > 0) {
const queryString = new URLSearchParams(params).toString()
url += '?' + queryString
}
return uni.navigateTo({ url })
}
// 使用
navigateTo('PRODUCT_DETAIL', { id: 123, from: 'home' })2. 錯誤處理
javascript
async function safeNavigate(options) {
try {
const result = await uni.navigateTo(options)
return result
} catch (error) {
console.error('路由跳轉失敗:', error)
// 錯誤處理
if (error.errMsg.includes('fail page')) {
uni.showToast({
title: '頁面不存在',
icon: 'error'
})
} else if (error.errMsg.includes('network')) {
uni.showToast({
title: '網路錯誤',
icon: 'error'
})
}
throw error
}
}3. 效能優化
javascript
// 防抖跳轉
let lastNavigateTime = 0
const NAVIGATE_DEBOUNCE = 500
function debounceNavigate(options) {
const now = Date.now()
if (now - lastNavigateTime < NAVIGATE_DEBOUNCE) {
return Promise.resolve()
}
lastNavigateTime = now
return uni.navigateTo(options)
}
// 預載入頁面
function preloadPage(url) {
// 提前載入頁面資源
uni.preloadPage({ url })
}常見問題
Q: navigateTo 和 redirectTo 有什麼區別?
A: navigateTo 保留當前頁面(可返回),redirectTo 關閉當前頁面(不可返回)。
Q: 如何傳遞複雜物件?
A: 可以使用全域事件、Vuex 或本地儲存傳遞複雜物件。
Q: 如何實現頁面間通訊?
A: 可以使用 uni.$emit/uni.$on 事件匯流排或 Vuex 狀態管理。
Q: 路由跳轉有數量限制嗎?
A: 是的,小程式平臺通常有10層頁面棧限制。
掌握路由系統是開發高效uni-app應用的關鍵!