導航組件
導航組件是用於頁面導航和佈局的組件,在 uni-app 中提供了多種導航組件,幫助開發者建構應用的導航結構。
navigator 頁面連結
navigator 組件類似於 HTML 中的 <a> 標籤,用於頁面跳轉。該組件會自動根據系統平台呼叫對應的跳轉方式。
屬性說明
| 屬性名 | 類型 | 預設值 | 說明 |
|---|---|---|---|
| url | String | 應用內的跳轉連結,值為相對路徑或絕對路徑 | |
| open-type | String | navigate | 跳轉方式,可選值:navigate、redirect、switchTab、reLaunch、navigateBack |
| delta | Number | 1 | 當 open-type 為 'navigateBack' 時有效,表示回退的層數 |
| animation-type | String | pop-in/out | 當 open-type 為 navigate、navigateBack 時有效,視窗的顯示/關閉動畫效果 |
| animation-duration | Number | 300 | 當 open-type 為 navigate、navigateBack 時有效,視窗顯示/關閉動畫的持續時間 |
| hover-class | String | navigator-hover | 指定點擊時的樣式類,當 hover-class="none" 時,沒有點擊態效果 |
open-type 有效值
| 值 | 說明 | 平台差異說明 |
|---|---|---|
| navigate | 對應 uni.navigateTo 的功能 | |
| redirect | 對應 uni.redirectTo 的功能 | |
| switchTab | 對應 uni.switchTab 的功能 | |
| reLaunch | 對應 uni.reLaunch 的功能 | |
| navigateBack | 對應 uni.navigateBack 的功能 | |
| exit | 退出小程式 | 微信小程式、QQ小程式 |
範例程式碼
<template>
<view>
<!-- 普通跳轉 -->
<navigator url="/pages/detail/detail?id=1">跳轉到詳情頁</navigator>
<!-- 使用 redirect 方式跳轉 -->
<navigator url="/pages/about/about" open-type="redirect">重新導向到關於頁面</navigator>
<!-- 跳轉到 tabBar 頁面 -->
<navigator url="/pages/index/index" open-type="switchTab">切換到首頁</navigator>
<!-- 返回上一頁 -->
<navigator open-type="navigateBack">返回上一頁</navigator>
<!-- 自訂點擊態樣式 -->
<navigator url="/pages/detail/detail" hover-class="custom-hover">
自訂點擊態效果
</navigator>
</view>
</template>
<style>
.custom-hover {
background-color: #f0f0f0;
opacity: 0.8;
}
</style>2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
頁面導航 API
除了使用 navigator 組件進行頁面導航外,uni-app 還提供了一系列導航 API,可以在 JavaScript 中實現頁面跳轉。
uni.navigateTo(OBJECT)
保留當前頁面,跳轉到應用內的某個頁面,使用 uni.navigateBack 可以返回到原頁面。
// 跳轉到詳情頁
uni.navigateTo({
url: '/pages/detail/detail?id=1',
success: function(res) {
console.log('跳轉成功');
},
fail: function(err) {
console.error('跳轉失敗', err);
},
complete: function() {
console.log('跳轉完成');
}
});2
3
4
5
6
7
8
9
10
11
12
13
uni.redirectTo(OBJECT)
關閉當前頁面,跳轉到應用內的某個頁面。
// 重新導向到關於頁面
uni.redirectTo({
url: '/pages/about/about'
});2
3
4
uni.reLaunch(OBJECT)
關閉所有頁面,開啟到應用內的某個頁面。
// 重新啟動到首頁
uni.reLaunch({
url: '/pages/index/index'
});2
3
4
uni.switchTab(OBJECT)
跳轉到 tabBar 頁面,並關閉其他所有非 tabBar 頁面。
// 切換到首頁 Tab
uni.switchTab({
url: '/pages/index/index'
});2
3
4
uni.navigateBack(OBJECT)
關閉當前頁面,返回上一頁面或多級頁面。
// 返回上一頁
uni.navigateBack();
// 返回到指定層級頁面
uni.navigateBack({
delta: 2 // 返回的頁面數,如果 delta 大於現有頁面數,則返回到首頁
});2
3
4
5
6
7
頁面堆疊
uni-app 的頁面堆疊最多十層,當頁面堆疊達到十層後,uni.navigateTo 不能正常跳轉,此時可以使用 uni.redirectTo 或 uni.reLaunch 等方法替代。
可以使用 getCurrentPages() 函數取得當前頁面堆疊的實例,以陣列形式按堆疊的順序給出,第一個元素為首頁,最後一個元素為當前頁面。
// 取得頁面堆疊
const pages = getCurrentPages();
// 取得當前頁面
const currentPage = pages[pages.length - 1];
// 取得當前頁面的路由
const currentRoute = currentPage.route;
// 取得當前頁面的參數
const currentOptions = currentPage.options;
console.log('當前頁面路由:', currentRoute);
console.log('當前頁面參數:', currentOptions);2
3
4
5
6
7
8
9
10
11
自訂導航列
在某些場景下,我們可能需要自訂導航列來實現特定的設計需求。uni-app 支援透過設定 pages.json 來設定導航列樣式或隱藏原生導航列,然後使用自訂組件實現導航列。
隱藏原生導航列
在 pages.json 中設定:
{
"pages": [
{
"path": "pages/index/index",
"style": {
"navigationStyle": "custom" // 隱藏原生導航列
}
}
]
}2
3
4
5
6
7
8
9
10
自訂導航列組件範例
<template>
<view class="custom-nav" :style="{ paddingTop: statusBarHeight + 'px' }">
<view class="nav-content">
<view class="nav-left" @click="goBack" v-if="showBack">
<text class="iconfont icon-back"></text>
</view>
<view class="nav-title">
<text>{{ title }}</text>
</view>
<view class="nav-right">
<slot name="right"></slot>
</view>
</view>
</view>
</template>
<script>
export default {
props: {
title: {
type: String,
default: '標題'
},
showBack: {
type: Boolean,
default: true
}
},
data() {
return {
statusBarHeight: 0
}
},
created() {
// 取得狀態列高度
const systemInfo = uni.getSystemInfoSync();
this.statusBarHeight = systemInfo.statusBarHeight;
},
methods: {
goBack() {
uni.navigateBack({
delta: 1
});
}
}
}
</script>
<style scoped>
.custom-nav {
position: fixed;
top: 0;
left: 0;
width: 100%;
background-color: #ffffff;
z-index: 999;
box-shadow: 0 1px 6px rgba(0, 0, 0, 0.1);
}
.nav-content {
display: flex;
align-items: center;
height: 44px;
padding: 0 15px;
}
.nav-left {
width: 60px;
display: flex;
align-items: center;
}
.nav-title {
flex: 1;
text-align: center;
font-size: 16px;
font-weight: bold;
}
.nav-right {
width: 60px;
display: flex;
justify-content: flex-end;
align-items: center;
}
.icon-back {
font-size: 20px;
}
</style>2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
使用自訂導航列:
<template>
<view class="container">
<custom-nav title="自訂導航列">
<template #right>
<text class="iconfont icon-share" @click="share"></text>
</template>
</custom-nav>
<view class="content" :style="{ paddingTop: navHeight + 'px' }">
<!-- 頁面內容 -->
<view>頁面內容區域</view>
</view>
</view>
</template>
<script>
import CustomNav from '@/components/CustomNav.vue';
export default {
components: {
CustomNav
},
data() {
return {
navHeight: 0
}
},
created() {
// 計算導航列高度
const systemInfo = uni.getSystemInfoSync();
this.navHeight = systemInfo.statusBarHeight + 44;
},
methods: {
share() {
uni.showToast({
title: '分享功能'
});
}
}
}
</script>2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
TabBar 導航
TabBar 是行動應用常用的底部導航列,uni-app 支援透過設定 pages.json 來設定應用的 TabBar。
TabBar 設定範例
{
"tabBar": {
"color": "#7A7E83",
"selectedColor": "#3cc51f",
"borderStyle": "black",
"backgroundColor": "#ffffff",
"list": [
{
"pagePath": "pages/index/index",
"iconPath": "static/images/home.png",
"selectedIconPath": "static/images/home-active.png",
"text": "首頁"
},
{
"pagePath": "pages/category/category",
"iconPath": "static/images/category.png",
"selectedIconPath": "static/images/category-active.png",
"text": "分類"
},
{
"pagePath": "pages/cart/cart",
"iconPath": "static/images/cart.png",
"selectedIconPath": "static/images/cart-active.png",
"text": "購物車"
},
{
"pagePath": "pages/user/user",
"iconPath": "static/images/user.png",
"selectedIconPath": "static/images/user-active.png",
"text": "我的"
}
]
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
TabBar 屬性說明
| 屬性 | 類型 | 必填 | 預設值 | 說明 |
|---|---|---|---|---|
| color | HexColor | 是 | tab 上的文字預設顏色 | |
| selectedColor | HexColor | 是 | tab 上的文字選中時的顏色 | |
| backgroundColor | HexColor | 是 | tab 的背景色 | |
| borderStyle | String | 否 | black | tabbar 上邊框的顏色,可選值:black、white |
| list | Array | 是 | tab 的列表,最少 2 個、最多 5 個 tab | |
| position | String | 否 | bottom | tabBar 的位置,可選值:bottom、top |
| custom | Boolean | 否 | false | 是否自訂 tabBar |
自訂 TabBar
對於需要更複雜互動或特殊樣式的 TabBar,uni-app 支援自訂 TabBar。
- 首先在
pages.json中設定custom: true:
{
"tabBar": {
"custom": true,
"color": "#7A7E83",
"selectedColor": "#3cc51f",
"borderStyle": "black",
"backgroundColor": "#ffffff",
"list": [
{
"pagePath": "pages/index/index",
"text": "首頁"
},
{
"pagePath": "pages/category/category",
"text": "分類"
},
{
"pagePath": "pages/cart/cart",
"text": "購物車"
},
{
"pagePath": "pages/user/user",
"text": "我的"
}
]
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
- 建立自訂 TabBar 組件:
<!-- components/CustomTabBar.vue -->
<template>
<view class="custom-tab-bar">
<view
v-for="(item, index) in tabList"
:key="index"
class="tab-item"
:class="{ active: current === index }"
@click="switchTab(item.pagePath, index)"
>
<view class="icon-wrapper">
<image :src="current === index ? item.selectedIconPath : item.iconPath" class="icon"></image>
<view v-if="item.badge" class="badge">{{item.badge}}</view>
</view>
<text class="text">{{item.text}}</text>
</view>
</view>
</template>
<script>
export default {
data() {
return {
current: 0,
tabList: [
{
pagePath: '/pages/index/index',
iconPath: '/static/images/home.png',
selectedIconPath: '/static/images/home-active.png',
text: '首頁',
badge: ''
},
{
pagePath: '/pages/category/category',
iconPath: '/static/images/category.png',
selectedIconPath: '/static/images/category-active.png',
text: '分類',
badge: ''
},
{
pagePath: '/pages/cart/cart',
iconPath: '/static/images/cart.png',
selectedIconPath: '/static/images/cart-active.png',
text: '購物車',
badge: '5'
},
{
pagePath: '/pages/user/user',
iconPath: '/static/images/user.png',
selectedIconPath: '/static/images/user-active.png',
text: '我的',
badge: ''
}
]
}
},
methods: {
switchTab(path, index) {
if (this.current !== index) {
uni.switchTab({
url: path
});
this.current = index;
}
},
setTabBarBadge(index, text) {
this.tabList[index].badge = text;
}
},
created() {
// 取得當前頁面路徑,設定當前選中的 tab
const pages = getCurrentPages();
const page = pages[pages.length - 1];
const currentPath = '/' + page.route;
this.tabList.forEach((item, index) => {
if (item.pagePath === currentPath) {
this.current = index;
}
});
}
}
</script>
<style scoped>
.custom-tab-bar {
position: fixed;
bottom: 0;
left: 0;
right: 0;
height: 50px;
background-color: #ffffff;
display: flex;
box-shadow: 0 -1px 5px rgba(0, 0, 0, 0.1);
}
.tab-item {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.icon-wrapper {
position: relative;
}
.icon {
width: 24px;
height: 24px;
}
.badge {
position: absolute;
top: -8px;
right: -8px;
background-color: #ff4d4f;
color: #ffffff;
font-size: 12px;
min-width: 16px;
height: 16px;
line-height: 16px;
text-align: center;
border-radius: 8px;
padding: 0 4px;
}
.text {
font-size: 12px;
margin-top: 2px;
}
.active .text {
color: #3cc51f;
}
</style>2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
- 在每個 TabBar 頁面中引入自訂 TabBar 組件:
<template>
<view class="container">
<!-- 頁面內容 -->
<view class="content">
<text>首頁內容</text>
</view>
<!-- 自訂 TabBar -->
<custom-tab-bar></custom-tab-bar>
</view>
</template>
<script>
import CustomTabBar from '@/components/CustomTabBar.vue';
export default {
components: {
CustomTabBar
}
}
</script>
<style>
.container {
padding-bottom: 50px; /* 為 TabBar 預留空間 */
}
</style>2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
側滑導航
對於一些內容豐富的應用,側滑導航是一種常見的導航方式。uni-app 沒有內建側滑導航組件,但可以透過自訂組件實現。
側滑導航範例
<template>
<view class="container">
<!-- 遮罩層 -->
<view
class="mask"
:class="{ show: isOpen }"
@click="toggleDrawer"
@touchmove.stop.prevent
></view>
<!-- 側滑選單 -->
<view class="drawer" :class="{ open: isOpen }">
<view class="drawer-header">
<image class="avatar" src="/static/images/avatar.png"></image>
<view class="user-info">
<text class="username">使用者名稱</text>
<text class="desc">個人簡介</text>
</view>
</view>
<view class="drawer-content">
<view
v-for="(item, index) in menuList"
:key="index"
class="menu-item"
:class="{ active: activeMenu === index }"
@click="selectMenu(item, index)"
>
<text class="iconfont" :class="item.icon"></text>
<text class="menu-text">{{item.text}}</text>
</view>
</view>
<view class="drawer-footer">
<view class="menu-item" @click="logout">
<text class="iconfont icon-logout"></text>
<text class="menu-text">退出登入</text>
</view>
</view>
</view>
<!-- 主內容區 -->
<view class="main-content">
<view class="header">
<text class="iconfont icon-menu" @click="toggleDrawer"></text>
<text class="title">{{title}}</text>
<text class="placeholder"></text>
</view>
<view class="content">
<slot></slot>
</view>
</view>
</view>
</template>
<script>
export default {
props: {
title: {
type: String,
default: '標題'
}
},
data() {
return {
isOpen: false,
activeMenu: 0,
menuList: [
{ text: '首頁', icon: 'icon-home', path: '/pages/index/index' },
{ text: '訊息', icon: 'icon-message', path: '/pages/message/message' },
{ text: '收藏', icon: 'icon-star', path: '/pages/favorite/favorite' },
{ text: '設定', icon: 'icon-setting', path: '/pages/setting/setting' }
]
}
},
methods: {
toggleDrawer() {
this.isOpen = !this.isOpen;
},
selectMenu(item, index) {
this.activeMenu = index;
this.isOpen = false;
// 跳轉到對應頁面
uni.navigateTo({
url: item.path
});
},
logout() {
uni.showModal({
title: '提示',
content: '確定要退出登入嗎?',
success: (res) => {
if (res.confirm) {
// 執行退出登入操作
uni.showToast({
title: '已退出登入'
});
// 跳轉到登入頁
setTimeout(() => {
uni.reLaunch({
url: '/pages/login/login'
});
}, 1500);
}
}
});
}
}
}
</script>
<style scoped>
.container {
position: relative;
width: 100%;
height: 100vh;
overflow: hidden;
}
/* 遮罩層 */
.mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
z-index: 998;
opacity: 0;
visibility: hidden;
transition: all 0.3s;
}
.mask.show {
opacity: 1;
visibility: visible;
}
/* 側滑選單 */
.drawer {
position: fixed;
top: 0;
left: -80%;
width: 80%;
height: 100%;
background-color: #ffffff;
z-index: 999;
transition: all 0.3s;
display: flex;
flex-direction: column;
}
.drawer.open {
left: 0;
}
.drawer-header {
padding: 40px 20px 20px;
background-color: #3cc51f;
display: flex;
align-items: center;
}
.avatar {
width: 60px;
height: 60px;
border-radius: 30px;
margin-right: 15px;
}
.user-info {
color: #ffffff;
}
.username {
font-size: 18px;
font-weight: bold;
}
.desc {
font-size: 14px;
margin-top: 5px;
}
.drawer-content {
flex: 1;
padding: 10px 0;
}
.menu-item {
height: 50px;
display: flex;
align-items: center;
padding: 0 20px;
}
.menu-item.active {
background-color: #f0f0f0;
}
.iconfont {
font-size: 20px;
margin-right: 10px;
}
.menu-text {
font-size: 16px;
}
.drawer-footer {
padding: 10px 0;
border-top: 1px solid #f0f0f0;
}
/* 主內容區 */
.main-content {
position: relative;
width: 100%;
height: 100%;
transition: all 0.3s;
}
.header {
height: 44px;
background-color: #3cc51f;
color: #ffffff;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 15px;
}
.title {
font-size: 18px;
font-weight: bold;
}
.placeholder {
width: 24px;
}
.content {
padding: 15px;
}
</style>2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
使用側滑導航:
<template>
<drawer-layout title="首頁">
<view>
<text>這是首頁內容</text>
</view>
</drawer-layout>
</template>
<script>
import DrawerLayout from '@/components/DrawerLayout.vue';
export default {
components: {
DrawerLayout
}
}
</script>2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
最佳實務
合理使用導航方式:根據應用的複雜度和使用者習慣選擇合適的導航方式,簡單應用可以使用 TabBar,複雜應用可以考慮側滑導航。
注意頁面堆疊管理:避免頁面堆疊過深導致無法繼續跳轉,合理使用 redirectTo 和 reLaunch。
最佳化導航體驗:新增適當的過渡動畫,提供清晰的導航提示,讓使用者知道當前位置和可以去哪裡。
處理好返回邏輯:特別是在多級頁面跳轉後,確保使用者可以方便地返回到合適的頁面。
保持導航一致性:在整個應用中保持導航風格的一致性,避免使用者困惑。
適配不同平台:注意導航組件在不同平台上的表現差異,做好相容處理。
常見問題
1. 頁面跳轉參數傳遞
在頁面跳轉時,可以透過 URL 參數傳遞資料:
// 在 A 頁面中跳轉到 B 頁面並傳遞參數
uni.navigateTo({
url: '/pages/B/B?id=1&name=uniapp'
});
// 在 B 頁面中接收參數
export default {
onLoad(options) {
console.log(options.id); // 1
console.log(options.name); // uniapp
}
}2
3
4
5
6
7
8
9
10
11
12
對於複雜資料,可以使用 encodeURIComponent 和 JSON.stringify 進行處理:
// 傳遞複雜資料
const data = { id: 1, list: [1, 2, 3], user: { name: 'uniapp' } };
uni.navigateTo({
url: `/pages/B/B?data=${encodeURIComponent(JSON.stringify(data))}`
});
// 接收複雜資料
export default {
onLoad(options) {
if (options.data) {
const data = JSON.parse(decodeURIComponent(options.data));
console.log(data);
}
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
2. 導航列樣式自訂
除了完全自訂導航列外,uni-app 也支援透過 pages.json 設定修改原生導航列的樣式:
{
"pages": [
{
"path": "pages/index/index",
"style": {
"navigationBarTitleText": "首頁",
"navigationBarBackgroundColor": "#3cc51f",
"navigationBarTextStyle": "white"
}
}
]
}2
3
4
5
6
7
8
9
10
11
12
3. 動態設定導航列標題
可以使用 uni.setNavigationBarTitle 方法動態設定當前頁面的導航列標題:
// 動態設定導航列標題
uni.setNavigationBarTitle({
title: '新標題'
});2
3
4
4. 處理實體返回鍵
在 Android 平台,使用者可能會使用實體返回鍵返回上一頁。可以透過監聽 onBackPress 事件來處理:
export default {
onBackPress() {
// 回傳 true 表示自己處理返回邏輯,不向下傳遞
// 回傳 false 表示不處理,由系統處理
if (this.needConfirm) {
uni.showModal({
title: '提示',
content: '確定要返回嗎?未儲存的內容將遺失',
success: (res) => {
if (res.confirm) {
uni.navigateBack();
}
}
});
return true; // 自己處理返回邏輯
}
return false; // 由系統處理返回邏輯
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19