导航组件
导航组件是用于页面导航和布局的组件,在 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